// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose:  Implementation of CXFormsXFormsElement.
//   $Id$

// XFormsXFormsElement.cpp : Implementation of CXFormsApp and DLL registration.

#include "stdafx.h"
#include "XForms.h"
#include "XFormsApp.h"
#include "FormControl.h"

#include "onloadhandler.h"
#include "Model.h"
#include "ModelUI.h"
#include "XFormsXFormsElement.h"
#include "XFormsElement.h"
#include "XFormsXPathFunctions.h"
#include <limits>
#include <algorithm>

#include "MessageDialog.h"
#include "dom3Core.h"
#include "FP_Definitions.h"
#include <typeinfo>
#include <Common/ImplReg/msw/ImplRegUtils.h>
#include "MSHTMLCSS3Handler.h"
#include "ImplGroup.h"
#include "repeat.h"
#include "TempDocImpl.h"
#include <HTMLDOMTranslator.h>
#include "TabletPCUtil.h"
#include "Submission.h"
#include "implInstance.h"
#include "UIImplRepository.h"
#include <Common/ResourceHandling/msw/loadHTMLResource.h>
#include <common/FileSystem/msw/FileSystemUtils.h>
#include <ActiveScript_i.c>
extern CXFormsApp theApp;

/////////////////////////////////////////////////////////////////////////////
//
//CMapWordToPtr CXFormsXFormsElement::m_EphemeralMap;

CXFormsXFormsElement::CXFormsXFormsElement(IHTMLDocument2 * pDoc)
:
	m_ModelList(1)
	,m_SubmitInfoList(1)
	,m_sCurrentURL(0)
	,m_pOwnerDocument(pDoc)
	,m_bDocumentComplete(false)
	,m_bDocumentLoaded(false)
	,m_bFatalErrorEncountered(false)
	,m_pFuncLib(NULL)
	,m_pNSResolver(NULL)
	,m_lPendingElements(0)
	,m_IdResolver(NULL,this)
	,m_pXPathIdResolver(0)
	,m_bAmAddingControls(false)
	,m_bDocumentInitialised(false)
	,m_pSplash(0)
	,m_bShutDown(false)
	,m_pDefaultModel(0)
	,m_lCssLength(0)
	,m_iUIWrapperLen(0)
	,m_lPendingModels(0)
	,m_sRepeatIterationWrapper(0)
	,m_bConfigured(false)
	,m_bBranded(false)
	,m_bSystrayInvoked(false)
	,m_bModalMessageActive(false)
	,m_lFormKey(0)
	,m_ControlsIndex(0)
	,m_ulDeferredUpdateCnt(0)
	,m_pCSSHandler(0)
	,m_pUIImplRepository(0)
	,m_pDocImpl(0)
	,m_pHasFeature(0)
	,m_pGetFeature(0)
	,m_pDocumentEvent(0)
	,m_pValueElementStringGenerator(0)
	,m_pDocEventSink(0)
	,m_bPerformingCaretHack(false)
	

{
	AFX_MANAGE_STATE(AfxGetStaticModuleState( ));
	AtlTrace("'formsplayer.dll': CXFormsXFormsElement %X created\n",this);
	
	IncreaseDeferredUpdateCnt(); //Paired in onDocumentComplete
	
	if(!theApp.m_pXForms)
		theApp.m_pXForms = this;
	
	try {
/*////////////////////
		CComPtr<IHTMLWindow2> pWin;
				m_pOwnerDocument->get_parentWindow(&pWin);
				CComQIPtr<IHTMLWindow4> pWin4(pWin);
				CComPtr<IDispatch> pDisp;
				CComVariant vtArg;
				pWin4->createPopup(&vtArg,&pDisp);
				m_pPop = pDisp;
				
///////////////////*/
		HRESULT hr;
		CComQIPtr<IDispatchEx> pDispEx = pDoc;
		if(pDispEx) {
			// Add document.createEvent()
			CDocumentEventCreateEvent::CreateInstance(&m_pDocumentEvent);
			if(m_pDocumentEvent) {
				m_pDocumentEvent->Initialise();
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"createEvent"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp(m_pDocumentEvent);
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}
			}

			// Get an appropriate DOMImplementation object.
			CComPtr<IDOMImplementation> pDocImpl;
			m_pDocImpl = new CTempDocImpl;
			m_pDocImpl->GetImplementation(&pDocImpl);

			// Add document.hasFeature()
			CDocumentImplementationHasFeature::CreateInstance(&m_pHasFeature);
			if(m_pHasFeature) {
				m_pHasFeature->Initialise(pDocImpl);
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"hasFeature"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp(m_pHasFeature);
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}
			}

			// Add document.getFeature()
			CDocumentImplementationGetFeature::CreateInstance(&m_pGetFeature);
			if(m_pGetFeature) {
				m_pGetFeature->Initialise(pDocImpl);
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"getFeature"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp(m_pGetFeature);
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}
			}
		}
	}
	catch(...) {
	}
	// Bug 136: Check for required components
	HRESULT hr = S_OK;//theApp.FormsManager().TestForRequiredComponents();

	if(canContinue()) 
	{
		//Report returned error from TestForRequiredComponents();
		if(FAILED(hr))
			// inside canContinue() because reportException() 
			//		may have been called from TestForRequiredComponents()
			reportException(hr);

		registerUnload();
		hr = theApp.InitForUse();

		if(SUCCEEDED(hr) && m_pOwnerDocument)
		{

			//make sure we don't lose our pointer to the document.
			//No need, smartPtr
			//m_pOwnerDocument->AddRef();
			
			hr = ResolveDocumentURL();
			hr = CreateScriptaculousLibrary();
			hr = CreatePanelLibrary();
			hr = CreateSelect1MinimalLibrary();
			hr = AddFormsPlayerExecutionContext();
			//Naughty Rico, you make the drag-selection of text not work.
			//hr = CreateRicoLibrary();
			//save the top-level namespaces, this is to pass on to control viewlink documents
			hr = serializeNamespaces();
			//create the facility for namespace resolution
			if(S_OK == hr) 
			{
				createNSResolver();
			}

			if(canContinue())
			{	
				//moved to CXFormsManager
				//loadGlobalSchemaCache(); 
				//create the function library for this form.
				CComObject<CXFormsXPathFunctions>::CreateInstance(&m_pFuncLib);
				m_pFuncLib->AddRef();
				m_pFuncLib->m_pOwnerForm = this; // Reciprocal relationship	

				CComBSTR sVersion;
				// Bug 738: Inform the CXFormsConfig object of the current formsPlayer version.
				if(m_pFuncLib) {
					m_pFuncLib->property(CComBSTR(L"FormsPlayerVersion"), &sVersion);
					m_XFConfig.CurrentVersion(sVersion);
				}

				theApp.FormsManager().Initialise(sVersion);
//				theApp.FormsManager().SystrayAddref();

				loadConfigFile();
			}
		}
	}
	// Superceded by bug 267...
	//m_bBrandingRequired = CheckRegistryForBrandingKeys();

	// Get an appropriate DOMImplementation object.
	CComPtr<IDOMImplementation> pDocImpl;
	if(!m_pDocImpl)
		m_pDocImpl = new CTempDocImpl;
	if(m_pDocImpl)
		m_pDocImpl->GetImplementation(&pDocImpl);

	CComQIPtr<IDispatchEx> pDispEx = m_pOwnerDocument;
	if(pDispEx) {
		DISPID id = 0;
		// Add a DOMImplementation object to the document.
		hr = pDispEx->GetDispID(CComBSTR(L"DOMImplementation"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
		// Commented code was my attempt to remove the Microsoft document.implementation
		// property and replace it with our own. The attempt failed.
		//hr = pDispEx->GetDispID(CComBSTR(L"implementation"), fdexNameCaseInsensitive, &id);
		if(SUCCEEDED(hr)) {
		//	pDispEx->DeleteMemberByDispID(id);
		//	pDispEx->GetDispID(CComBSTR(L"implementation"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
		//	CComVariant vDisp = pDocImpl;
		//	DISPID putid = DISPID_PROPERTYPUT;
		//	DISPPARAMS dparams;
		//	dparams.rgvarg = &vDisp;
		//	dparams.rgdispidNamedArgs = &putid;
		//	dparams.cArgs = 1;
		//	dparams.cNamedArgs = 1;
		//	pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dparams, 0, 0, 0);
		//} else if(hr == DISP_E_UNKNOWNNAME) {
		//	pDispEx->GetDispID(CComBSTR(L"implementation"), fdexNameCaseInsensitive | fdexNameEnsure, &id);
			CComVariant vDisp = pDocImpl;
			DISPID putid = DISPID_PROPERTYPUT;
			DISPPARAMS dparams;
			dparams.rgvarg = &vDisp;
			dparams.rgdispidNamedArgs = &putid;
			dparams.cArgs = 1;
			dparams.cNamedArgs = 1;
			pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dparams, 0, 0, 0);
		}
		
	}
	
	AddCaretHidingHack();
}

HRESULT CXFormsXFormsElement::handleEvent(DISPID eventID,VARIANT* pvarResult,DISPPARAMS* pdispparams)
{
	HRESULT hr = S_FALSE;
	if(eventID == 0)
	{
		if(!m_bPerformingCaretHack)
		{
			if(pdispparams->rgvarg && pdispparams->rgvarg->vt == VT_DISPATCH)
			{
				CComQIPtr<IHTMLEventObj2> pEvObj = pdispparams->rgvarg->pdispVal;
				if(pEvObj) 
				{
					CComBSTR s;
					pEvObj->get_type(&s);
					if(s == L"beforeactivate")
					{
						hr = CaretHidingHack(pEvObj);
					}
					else
					{
						hr = E_NOTIMPL;
					}
				}
			}
		}
		else
		{
			hr = E_INVALIDARG;
		}
	}
	else
	{
		hr = E_NOTIMPL;
	}
	return hr;
}

HRESULT CXFormsXFormsElement::CaretHidingHack(CComPtr<IHTMLEventObj2> &pEvObj)
{

	HRESULT hr;
	//If the secret caret stealer has not been inserted into the document, do so now.
	if(!m_pSecretCaretStealer)
	{
		CComPtr<IHTMLElement> pBody;
		m_pOwnerDocument->get_body(&pBody);
		m_pSecretCaretStealer =pBody;
	}

	if(m_pSecretCaretStealer)
	{
		CComPtr<IHTMLElement> pSrcEl;
		pEvObj->get_srcElement(&pSrcEl);
		//HTML buttons don't seem to take the caret away, which is annoying.
		CComQIPtr<IHTMLInputButtonElement> pSrcAsButton(pSrcEl);
		if(pSrcAsButton)
		{
			m_bPerformingCaretHack = true;
			//The caret stealing process consists of setting a top-level hidden input
			//	to be the active element in a document, just before the intended active
			//	element becomes active.  Then setting the intended recipient to active.
			CComQIPtr<IHTMLElement3> pSrcEl3(pSrcEl);
			hr = m_pSecretCaretStealer->setActive();
			hr = pSrcEl3->setActive();
			m_bPerformingCaretHack = false;
		}
		else
		{
			hr = S_FALSE;
		}
	}
	else
	{
		hr = E_FAIL;
	}

	return hr;
}

HRESULT CXFormsXFormsElement::AddCaretHidingHack()
{	
	HRESULT hr = S_FALSE;
	if(!m_pDocEventSink) 
	{
		CComObject<CEventSink> *pSink;
		CComObject<CEventSink>::CreateInstance(&pSink);

		if(pSink) 
		{
			pSink->SetHandler(this);
			pSink->m_bHandleThroughHandleEvent = true;
			m_pDocEventSink = pSink;
		}
	}

	if(m_pDocEventSink) 
	{
		CComQIPtr<IHTMLDocument3> pDoc3(m_pOwnerDocument);
		if(pDoc3)
		{
			VARIANT_BOOL bResult;
			hr = pDoc3->attachEvent(CComBSTR(L"onbeforeactivate"), m_pDocEventSink, &bResult);
		}
	}
	else
	{
		hr = E_FAIL;
		ASSERT(0);
	}
	return hr;
}

HRESULT CXFormsXFormsElement::AddFormsPlayerExecutionContext()
{
	char fullpath[MAX_PATH + 6] = "res://";
	::GetModuleFileName(_Module.m_hInst,&fullpath[6],MAX_PATH);
	CComBSTR bsFullPath = fullpath;

	HRESULT hr = S_OK;
	
    // Add the xblParentNode property to the viewlinks document which 
	// points to the master element.
	CComQIPtr<IDispatchEx> pDocDisp = m_pOwnerDocument;
	if(pDocDisp) {
		CComVariant vtPath = bsFullPath;
		// If I could get DISPATCH_PROPERTYPUTREF to work I could just use
		// this code below.
		// vtMasterEl.pdispVal = pMstDisp;

		// Create the property xblParentNode and get the dispid for it
		DISPID lDispID;
		hr = pDocDisp->GetDispID(CComBSTR(L"formsPlayerExecutionContextResURL"), fdexNameEnsure, &lDispID);
		if(SUCCEEDED(hr)) {
			DISPID lPutID = DISPID_PROPERTYPUT;
			DISPPARAMS dispparams;
			dispparams.rgvarg = &vtPath;
			dispparams.rgdispidNamedArgs = &lPutID;
			dispparams.cArgs = 1;
			dispparams.cNamedArgs = 1;
			// Set the value of the attribute xblParentNode using the dispid
			hr = pDocDisp->InvokeEx(lDispID, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL);
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::loadUIWrapper(void)
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())

	if(!m_pCSSHandler)
		addDefaultStylesheet();
	
	CComBSTR sCSS;
	long lCss = 0;
	getCSS(&sCSS,&lCss);

	// this method could simply be done using the formatstring(). 
	// This would require the property m_sImportDirectives being a CStringW.
	// Get the 
	//CComBSTR sUIWrapper;
	CStringW sUIWrapper;
	sUIWrapper.LoadString(IDS_XFormsElementGUI_HTMLDoc);
//	insertResourceLocation(sUIWrapper,false);
	//escape percents from places where they may occur
	CStringW sCurrentURL(m_sCurrentURL);
	sCurrentURL.Replace(L"%",L"%%");
	//escape percents from places where they may occur
	CStringW sCSS_escaped(sCSS);
	sCSS_escaped.Replace(L"%",L"%%");
	lCss = sCSS_escaped.GetLength();
	//
	// Cache length
	CStringW sObjectDeclaration;
	sObjectDeclaration.Format(theApp.m_sObjectDeclaration,m_lFormKey);
	m_iUIWrapperLen = m_lNamespaceDeclaration
			+
			sObjectDeclaration.GetLength()
			+
			m_lImportDirectives
			+
			sUIWrapper.GetLength()
			+ 
			lCss
			+
			sCurrentURL.GetLength();

	//insert all the bits into the right places in the string.

	wchar_t * wsVLDoc 
		=
		new wchar_t
		[
			m_iUIWrapperLen + 1
		];
	
	swprintf(wsVLDoc,sUIWrapper,m_sNamespaceDeclaration,sObjectDeclaration,m_sImportDirectives,sCSS_escaped ,sCurrentURL);

	m_sUIWrapper = wsVLDoc;

	delete[] wsVLDoc; 
	
	return S_OK;
}
#define CATCH_CONTROLCONTAINER_LIST_FLUSH_EXCEPTIONS_AT_SOURCE 1
#undef CATCH_CONTROLCONTAINER_LIST_FLUSH_EXCEPTIONS_AT_SOURCE

CXFormsXFormsElement::~CXFormsXFormsElement()
{
	AFX_MANAGE_STATE(AfxGetStaticModuleState())
	#if DEBUG_LEVEL & DEBUG_MONITOR_DESTRUCTION
		{
			CComBSTR sNewDocURLDebug;
			m_pOwnerDocument->get_URL(&sNewDocURLDebug);
			AtlTrace("'formsplayer.dll':destructor called - %S \n",sNewDocURLDebug);
		}
	#endif

	HRESULT hr;
	delete m_pValueElementStringGenerator;
	delete m_pUIImplRepository;
	theApp.FormsManager().removeForm(m_lFormKey);
	delete m_pCSSHandler;
	//notify models of impending destruction
	POSITION pos = m_ModelList.GetStartPosition();
	//These loops cannot be merged!!!  All models must be notified
	//	of impending destruction before any models are destroyed.
	//	merging the loops will cause a crash when someone wants 
	//	to perform actions on another model, or controls belonging
	//	 to it in the xforms-model-destruct phase.
	while (pos != NULL)
	{
		try
		{
			CImplModel* pImpl = NULL;
			CString string;

			m_ModelList.GetNextAssoc(pos, string, (void*&) pImpl);
		
			if(pImpl)
			{
				pImpl->notifyDestroy();
			}
		}
		catch(...){ASSERT(0);}


	}
	m_bShutDown = true;
	//Remove this form from the app's gloabl list and release
	//		our reference to the document.

	if(theApp.m_pXForms == this)
	{
		theApp.m_pXForms = 0;
	}

	hr = reset();

	::safeRelease(m_pFuncLib);

	while(!m_ClosingMessageWindows.IsEmpty())
	{
		try
		{
			delete m_ClosingMessageWindows.RemoveHead();
		}
		catch(...){ASSERT(0);}
	}

	while(!m_OpenMessageWindows.IsEmpty())
	{
		try
		{
			delete m_OpenMessageWindows.RemoveHead();
		}
		catch(...){ASSERT(0);}
	}
	pos = m_ControlContainers.GetStartPosition();
	LPVOID pvdKey,pvdVal;
	while(pos)
	{
#ifndef CATCH_CONTROLCONTAINER_LIST_FLUSH_EXCEPTIONS_AT_SOURCE
		try
		{
#endif
			m_ControlContainers.GetNextAssoc(pos,pvdKey,pvdVal);
			DeregisterControlContainer((IUnknown*)pvdKey);
			::safeRelease(((IUnknown *&)pvdKey));
#ifndef CATCH_CONTROLCONTAINER_LIST_FLUSH_EXCEPTIONS_AT_SOURCE
		}
		catch(...){ASSERT(0);}
#endif
	}
	m_ControlContainers.RemoveAll();
//don't delete it, it can only be cleaned up by the thread that created the window.
	delete m_pSplash;
	m_pSplash = 0;

	::safeRelease(m_pNSResolver);

	POSITION p = m_lstRepeatsBeingWaitedFor.GetHeadPosition();
	while(p) {
		try
		{
			delete[] m_lstRepeatsBeingWaitedFor.GetNext(p);
		}
		catch(...){ASSERT(0);}
	}
	m_lstRepeatsBeingWaitedFor.RemoveAll();

	// Destroy the default systray icon for this form.
//	if(m_pDefaultSysTrayIcon)
//		m_pDefaultSysTrayIcon.Release();

//	theApp.FormsManager().SystrayRelease();
	if(m_pDocImpl) {
		delete m_pDocImpl;
		m_pDocImpl = 0;
	}

	CComQIPtr<IDispatchEx> pDispEx = m_pOwnerDocument;
	CComPtr<IHTMLWindow2> pWinX;
	m_pOwnerDocument->get_parentWindow(&pWinX);

	if(m_pHasFeature) {
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"hasFeature"), fdexNameCaseInsensitive , &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp = 0;
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}
		m_pHasFeature->Release();
		m_pHasFeature = 0;
	}

	if(m_pGetFeature) {
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"getFeature"), fdexNameCaseInsensitive , &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp = 0;
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}
		m_pGetFeature->Release();
		m_pGetFeature = 0;
	}

	if(m_pDocumentEvent) {
				DISPID id = 0;
				hr = pDispEx->GetDispID(CComBSTR(L"createEvent"), fdexNameCaseInsensitive , &id);
				if(SUCCEEDED(hr)) {
					CComVariant vDisp = 0;
					DISPID putid = DISPID_PROPERTYPUT;
					DISPPARAMS dp;
					dp.rgvarg = &vDisp;
					dp.rgdispidNamedArgs = &putid;
					dp.cArgs = 1;
					dp.cNamedArgs = 1;
					pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
				}

		m_pDocumentEvent->Release();
		m_pDocumentEvent = 0;
	}

	{
		DISPID id = 0;
		hr = pDispEx->GetDispID(CComBSTR(L"DOMImplementation"), fdexNameCaseInsensitive , &id);
		if(SUCCEEDED(hr)) {
			CComVariant vDisp = 0;
			DISPID putid = DISPID_PROPERTYPUT;
			DISPPARAMS dp;
			dp.rgvarg = &vDisp;
			dp.rgdispidNamedArgs = &putid;
			dp.cArgs = 1;
			dp.cNamedArgs = 1;
			pDispEx->InvokeEx(id, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dp, 0, 0, 0);
		}
	}

	CComBSTR sScriptaculous(L"Scriptaculous");
	hr = RemoveScriptingObjectFromDocument(sScriptaculous,m_pOwnerDocument);
	CComBSTR sPanel(L"PANEL");	
	hr = RemoveScriptingObjectFromDocument(sPanel,m_pOwnerDocument);

	const CComBSTR doGC(L"CollectGarbage();");
	CComPtr<IHTMLWindow2> pWin;
	m_pOwnerDocument->get_parentWindow(&pWin);
	CComVariant v;
	pWin->execScript(doGC,L"JScript",&v);

	CComBSTR S1Min(L"S1Min");
	hr = RemoveScriptingObjectFromDocument(S1Min,m_pOwnerDocument);

	xport::TabletPCClearUp();
	
	m_lstPostModalActions.RemoveAll();
	NotifyDeletionListeners();
	FlushDeletionListeners();

	AtlTrace("'formsplayer.dll': CXFormsXFormsElement %X Destroyed. \n", this);	

}

STDMETHODIMP CXFormsXFormsElement::onload(void)
{
	m_bDocumentLoaded = true;
	if(m_bShutDown)
	{
		return E_FAIL;
	}
	else if(!m_bDocumentComplete)
	{
		if(0 == m_lPendingElements)
		{
			AtlTrace("'formsplayer.dll' %X: onload called, proceeding with initialization \n", this);
			//I have loaded the whole document, so if there
			//	are no outstanding elements, I can start UIInitialize,
			onDocumentComplete();
		}
		else
		{
			AtlTrace("'formsplayer.dll' %X: onload called, m_lPendingElements == %i\n",this,m_lPendingElements);
		}
	}
	else
	{
		AtlTrace("'formsplayer.dll' %X: onload called, document already complete ",this);
	}
	return S_OK;
}

//Model list clearance loop is wrapped in try{}catch(...){ASSERT(0);}, to ensure that 
//	an exception in an ealy iteration of the loop does not lead to massive leakage.
//	by preventing later iterations (and, indeed, all that follows)
//
//	if an unknown exception occurs that is caught by the catch(...), this will 
//	remove the undef below, this will remove the try/catch so that the exception 
//	will be left unhandled, and be reported 'at source' 

#define CATCH_MODEL_LIST_CLEARANCE_AT_SOURCE 1
//undef CATCH_MODEL_LIST_CLEARANCE_AT_SOURCE

#ifdef CATCH_MODEL_LIST_CLEARANCE_AT_SOURCE
	#define BEGIN_MODEL_LIST_CLEARANCE {
	#define END_MODEL_LIST_CLEARANCE }
#else
	#define BEGIN_MODEL_LIST_CLEARANCE try{
	#define END_MODEL_LIST_CLEARANCE }catch(...){ASSERT(0);}
#endif

HRESULT CXFormsXFormsElement::reset(void)
{

	HRESULT hr = S_OK;
	//Clear the XFormsPrefixes list 
	//	I don't know if this is needed,
	//	It should delete its members on delete.
	//m_lstXFormsPrefixes.RemoveAll();
	destroyUIChunks();
	try
	{
		//Clear any used error reports
		m_ErrorActionFactory.Clear();
	}
	catch(...)
	{
		ASSERT(0);
	}

	//delete the rubbish, may be used to delete everything at a later date.
	while(!m_HeapPointers.IsEmpty())
	{
		try
		{
			delete m_HeapPointers.RemoveHead();
		}
		catch(...){ASSERT(0);}

	}
	

	//Delete any now-extinct repeat iterations
	while(!m_IterationsToDelete.IsEmpty())
	{
		try
		{
			//This might be an assert:
			AtlTrace("'formsplayer.dll': Deleting repeat iteration on cleanup.\n");
			delete m_IterationsToDelete.RemoveHead();
		}
		catch(...){ASSERT(0);}

	}


	
	//NotifyDestroy may have invoked some asynchronous actions.
	//	Don't complete the destruction until they have completed.
	//This mechanism may be required by a future bug, but was not needed in order to fix
	//	the one it was originally implemented for.  In the interests of releasing this 
	//	soon, I have commented it out, as it may lead to problems in moving between forms.
	//	If there is a problem in the future with asynchronous
	//	submissions triggered by model-destruct, then this line should be uncommented.

	//AwaitLegalCloseOfForm();

	//delete the models.
	POSITION pos = m_ModelList.GetStartPosition();
	while (pos != NULL)
	{
		BEGIN_MODEL_LIST_CLEARANCE
		CImplModel* pImpl = NULL;
		CString string;

		m_ModelList.GetNextAssoc(pos, string, (void*&) pImpl);
	
		if(pImpl)
		{
			pImpl->m_pXFormsElement->m_pXForms = 0;
			pImpl->m_pXForms = 0;
			
			//Allow model to finish what it is doing before deleting it, otherwise it all gets fucked up
			LPARAM p = (LPARAM)this;
			const UINT msg_DeleteModel = WM_APP + 50;
			PostThreadMessage(GetCurrentThreadId(), msg_DeleteModel, 0, p);
			// msg-pump
			MSG msg;
			while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) 
			{
				if(msg.hwnd == NULL && msg.message == msg_DeleteModel && msg.lParam == p)
				{
					delete pImpl;
					break;
				}
				else if(msg.message == WM_TIMER)
				{
					delete pImpl;
					break;
				}
				else
				{
					TranslateMessage(&msg);
					DispatchMessage(&msg);
				}
			}
		}
		END_MODEL_LIST_CLEARANCE

	}
	m_ModelList.RemoveAll();
	//Clear the schemata lookup list.
	//This does nothing, but it doesn't leak either???
	while(pos)
	{

		try
		{
			CString string;
			LPDISPATCH pDisp;
			m_mapInlineSchemata.GetNextAssoc(pos, string, (void*&) pDisp);
		
			if(pDisp)
			{
				::safeRelease(pDisp);
			}
		}
		catch(...){ASSERT(0);}

	}
	m_mapInlineSchemata.RemoveAll();
	
	//delete the orphans
	// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
	while(!m_Orphans.IsEmpty())
	{
		CFormControl * pFC = NULL;
		pos = m_Orphans.GetStartPosition();
       	m_Orphans.GetNextAssoc(pos, pFC, pFC);
		m_Orphans.RemoveKey(pFC);
		//FUDGE: don't delete actions, they are currently deleted by 
		// their observer.  THis should not be the case.
		//	This is just here to prevent a crash.
		if(pFC) {
			try {
				CImplAction * pAct = NULL;
				pAct = dynamic_cast<CImplAction *>(pFC);
				if(pAct)
					pAct->m_pXForms = 0;
				else
					pFC->RequestDeleteAtEarliestConvenience();
			}
			catch(...) 
			{
				ASSERT(0);
			}
		}
	}
	

	pos = m_SubmitInfoList.GetStartPosition();
	while (pos != NULL)
	{
		try
		{
			CImplSubmitInfo * p = 0;
			//= m_SubmitInfoList.RemoveHead();	
			CString string;

			m_SubmitInfoList.GetNextAssoc(pos, string, (void*&) p);
			if(string[0])
				delete p;
		}
		catch(...){ASSERT(0);}
	}

	// delete any remaining controls.  This must not occur 
	//	before deleting live controls, as some of them
	//	have one-way pointers to controls with initPending.
	//	They may delete some initPending controls, which will
	//	remove them from this list.
	while(!m_lstInitPending.IsEmpty())
	{
		try
		{
			delete m_lstInitPending.RemoveHead();
		}
		catch(...){ASSERT(0);}
	}


	//delete all the map keys	
//	m_SubmitInfoList.RemoveAll();
	return hr;
}//CXFormsXFormsElement::reset


STDMETHODIMP CXFormsXFormsElement::loadURI(BSTR sHref, CImplActionLoadURI::EXlinkShow eShow)
{
	//The version W3C Working Draft 21 August 2002
	//	does not support all values of xlink:show
	//	in loadURI.

	HRESULT hr = S_OK;
	CComBSTR sURL;
	sURL.Attach(Relative2AbsolutePath(m_sCurrentURL,sHref));

	switch(eShow)
	{
		case CImplActionLoadURI::EXlinkShow::_new:
		{//use the browser's Navigate system.
			CComPtr<IHTMLWindow2> pWin;
			hr = m_pOwnerDocument->get_parentWindow(&pWin);
			if(SUCCEEDED(hr))
			{
				CComPtr<IHTMLWindow2> pNewWin;
				CComBSTR sEmpty;
				hr = pWin->open(sURL,sEmpty,sEmpty,false,&pNewWin);
			}
			break;
		}

		case CImplActionLoadURI::EXlinkShow::replace:
		{//use the browser's Navigate system.
			CComPtr<IHTMLWindow2> pWin;
			hr = m_pOwnerDocument->get_parentWindow(&pWin);
			if(SUCCEEDED(hr))
			{
				hr = pWin->navigate(sURL); 
			}
			break;
		}
		case CImplActionLoadURI::EXlinkShow::embed:
			break;
		case CImplActionLoadURI::EXlinkShow::other:
			hr = modalMessage(sURL, true);
			break;
		case CImplActionLoadURI::EXlinkShow::none:
			break;
		default:hr = E_FAIL;
	}
	return hr; 
}



HRESULT CXFormsXFormsElement::resetInstance(BSTR sModel) 
{
	//calls reset on the specified model.
	
	//lookup the model in the model list.
	CImplModel *  pModel = NULL;
	HRESULT hr = getModel(sModel,pModel);
	// it is an error to try to reset a non-model
	if(!pModel)
		hr = E_FAIL;
	else if(SUCCEEDED(hr))
	{
		//reset the model
		hr =  pModel->reset();
	}
	return hr;
}

HRESULT CXFormsXFormsElement::dispatch(BSTR sName,BSTR sTarget,bool bBubbles,bool bCancelable)
{
	//The spec does not define the behaviour for targets that are not ids,
	//	For now the behavour is to ignore.
	CImpl * pTarget = NULL;
	
	//retrive the target by its id.
	if(m_pXPathIdResolver)
		m_pXPathIdResolver->getItem(sTarget,pTarget);
	else
		m_IdResolver.getItem(sTarget,pTarget);

	if(pTarget)
	{
		//translate the eventName to a C++ string
		char * sEvent = BSTRToStr(sName);
		//post the event to the target.
		HRESULT hr =  pTarget->FireEvent(sEvent,bBubbles,bCancelable,NULL);
		delete[] sEvent;
		return hr;
	}
	else
	{
		return S_FALSE;
	}
	
}

STDMETHODIMP CXFormsXFormsElement::getModel(const BSTR sId, CImplModel*& ppModel)
{
	HRESULT hr = S_FALSE;
	//Nullify the model, this stops the final test succeeding on fail
	ppModel = NULL;
	if (sId && sId[0])
	{
		m_ModelList.Lookup(CString(sId), (void*&) ppModel);

		if(!ppModel && m_bAmAddingControls)
		{
			//Allow the calling function to know that 
			//	we failed because the model does not
			//	*yet* exist in the list.
			ppModel = NULL;
			//TODO: should be something like E_NOTREADY
			return E_ABORT;
		}
	}
	else if(m_pDefaultModel)
	{
		//In the absence of a modelID string, return the first model.
		//	This is incorrect behaviour as of W3C Working Draft 21 August 2002
		ppModel = m_pDefaultModel;
	}
	else if(m_bAmAddingControls)
	{
		ppModel = NULL;
		//TODO: should be something like E_NOTREADY
		return E_ABORT;
	}

	//return an error if ppModel is NULL
	if (ppModel)
	{
		hr = S_OK;
	}
	else
	{
		//should also fire the "xforms-binding-exception event" on a model.
		//	But I don't know which model, probably the first one.
		hr = FP_E_UNKNOWN_MODEL;
		::SetupErrorInfo(hr,sId);
		//Reporting the exception here to ensure setting of the fatal flag.
		reportException(hr);
	}

	return hr;
}

void CXFormsXFormsElement::AddAsyncRunner(IPotentiallyAsynchronousAction * pRunner)
{
	m_AsyncRunners.push_back(pRunner);
}

void CXFormsXFormsElement::RemoveAsyncRunner(IPotentiallyAsynchronousAction * pRunner)
{
	m_AsyncRunners.remove(pRunner);
}

void AwaitRunner(IPotentiallyAsynchronousAction * pRunner)
{
	pRunner->AwaitCompletion();
}

void CXFormsXFormsElement::AwaitLegalCloseOfForm()
{
	std::for_each(m_AsyncRunners.begin(),m_AsyncRunners.end(),AwaitRunner);
}

STDMETHODIMP CXFormsXFormsElement::getBind(const BSTR sId, CImplBind*& ppBind)
{
	HRESULT hr = S_FALSE;
	//Nullify the bind
	ppBind = NULL;

	if (m_Binds.Lookup(CString(sId), (void*&) ppBind))
		hr = S_OK;

	if(!ppBind)
	{
		if(m_bAmAddingControls)
		{
			//Allow the calling function to know that 
			//	we failed because the model does not
			//	*yet* exist in the list.
			ppBind = NULL;
			//TODO: should be something like E_NOTREADY
			hr = E_ABORT;
		}
		else
		{
			//should also fire the "xforms-binding-exception event" on a model.
			//	But I don't know which model, probably the first one.
			hr = FP_E_UNKNOWN_BIND;
			::SetupErrorInfo(hr,sId);
			reportException(hr);
		}
	}


	return hr;
}


STDMETHODIMP CXFormsXFormsElement::getRepeat(const BSTR sId, CImplRepeat** ppRepeat) 
{
	HRESULT hr = E_FAIL;

	*ppRepeat = NULL;
	if (sId)
	{
		CImpl * pImpl = 0;
		if(m_pXPathIdResolver)
			m_pXPathIdResolver->getItem(sId,pImpl);
		else
			m_IdResolver.getItem(sId,pImpl);

		if(pImpl)
		{
			*ppRepeat = dynamic_cast<CImplRepeat*>(pImpl);
		}
		else
		{
			*ppRepeat = NULL;
		}

	}
	
	if (*ppRepeat)
		hr = S_OK;
	else if(m_bDocumentInitialised)
	{
		//only report an error after initialisation,
		//	a repeat may nbot yet have been inserted.
		CString sErrorMessage;
		sErrorMessage.Format("repeat id=\"%S\" does not exist",sId);
		//reportError(sErrorMessage);
	} else {
		// Bug 110: This index() called from xf:bind attributes so we'll store the
		//          id attribute of the xf:repeat for later...
		POSITION p = m_lstRepeatsBeingWaitedFor.GetHeadPosition();
		wchar_t *buf;
		bool bAdd = true;
		while(p) {
			buf = m_lstRepeatsBeingWaitedFor.GetNext(p);
			if(wcscmp(buf, sId) == 0) {
				bAdd = false;
				break;
			}
		}
		if(bAdd) {
			wchar_t *s = new wchar_t[wcslen(sId) + 1];
			wcscpy(s, sId);
			m_lstRepeatsBeingWaitedFor.AddTail(s);
		}
		hr = S_FALSE;
	}
	return hr;
}

STDMETHODIMP CXFormsXFormsElement::getSubmitInfo(const BSTR sId, CImplSubmitInfo*& ppSubmitInfo) const 
{
	HRESULT hr = S_FALSE;

	if (sId && sId[0])
		m_SubmitInfoList.Lookup(CString(sId), (void*&) ppSubmitInfo);
	else
	{
		//Submitinfo defaulting to the first submit in the list.
		//	I don't know whether this is correct behaviour or not
		//	fails if there are no submitInfos.
		POSITION pos = m_SubmitInfoList.GetStartPosition();
		if(pos)
		{
			CString sTemp;
			m_SubmitInfoList.GetNextAssoc(pos, sTemp, (void*&) ppSubmitInfo);			
		}
		else
		{
			ppSubmitInfo = NULL;
		}
	}

	if (ppSubmitInfo)
		hr = S_OK;
	else
	{
		hr = FP_E_UNKNOWN_SUBMISSION;
		::SetupErrorInfo(hr,sId);
	}

	return hr;
}


HRESULT CXFormsXFormsElement::getInstance(BSTR InstanceId, CImplInstance *& ppInstance)
{
	//returns an instance object given its ID.
	//	returns S_FALSE when not found, but
	//	should probably return E_FAIL
	HRESULT hr = S_FALSE;

	ppInstance = NULL;
	if (InstanceId)
		m_Instances.Lookup(CString(InstanceId), (void*&) ppInstance);

	if (ppInstance)
		hr = S_OK;

	return hr;

}

STDMETHODIMP CXFormsXFormsElement::getModel(IUnknown * pUnkDocument, CImplModel *& ppModel) const
{
	//returns a model object given a document 
	//	which is one of its instances
	//	returns S_FALSE when not found.

	HRESULT hr = S_FALSE;

	ppModel = 0;
	if (pUnkDocument)
	{
		//Ensure that we are dealing with the unknown pointer.
		IUnknown *pRealUnk = 0;
		pUnkDocument->QueryInterface(IID_IUnknown,(void**)&pRealUnk);
		if(pRealUnk)
			m_MapDocToModel.Lookup(pRealUnk, (void*&) ppModel);
		::safeRelease(pRealUnk);
	}

	if (ppModel)
		hr = S_OK;

	return hr;

}

HRESULT CXFormsXFormsElement::recalculate()
{
	HRESULT hr = E_FAIL;
	if(canContinue())
	{
		//Refresh all the models in this form
		POSITION pos = m_ModelList.GetStartPosition();
		CImplModel * pModel = NULL;
		CString sKey;
		while(pos)
		{
			m_ModelList.GetNextAssoc(pos,sKey, (void*&) pModel);
			hr = pModel->doRecalculate();
			if(FAILED(hr))
				return hr;
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::rebuild(bool bForceRebuild)
{
	HRESULT hr = E_FAIL;
	if(canContinue())
	{
		//Refresh all the models in this form
		POSITION pos = m_ModelList.GetStartPosition();
		CImplModel * pModel = NULL;
		CString sKey;
		while(pos)
		{
			m_ModelList.GetNextAssoc(pos,sKey, (void*&) pModel);
			if(bForceRebuild)
				hr = pModel->rebuild(false);
			else
				hr = pModel->doRebuild();
			if(FAILED(hr))
				return hr;
		}
	}
	return hr;

}

HRESULT CXFormsXFormsElement::revalidate()
{
	HRESULT hr = E_FAIL;
	if(canContinue())
	{

		//Refresh all the models in this form
		POSITION pos = m_ModelList.GetStartPosition();
		CImplModel * pModel = NULL;
		CString sKey;
		while(pos)
		{
			m_ModelList.GetNextAssoc(pos,sKey, (void*&) pModel);
			hr = pModel->doRevalidate();
			if(FAILED(hr))
				return hr;
		}

	}
	return hr;

}

HRESULT CXFormsXFormsElement::refresh()
{

	HRESULT hr = E_FAIL; 
	//delete any hanging CMessageDialog objects
	while(!m_ClosingMessageWindows.IsEmpty())
	{
		delete m_ClosingMessageWindows.RemoveHead();
	}

	burnDeadControls();

	if(canContinue())
	{
		//orphans are quick to update, so should happen before
		//	bound data,  to give the illusion of faster processing.
		//	if we thread this, we may wish to run it
		//	after the bound update.  We need to
		//	watch out for selects with closed collections.
		hr = showNewOrphans();

		//Refresh all the models in this form
		POSITION pos = m_ModelList.GetStartPosition();
		CImplModel * pModel = NULL;
		CString sKey;
		while(pos)
		{
			m_ModelList.GetNextAssoc(pos,sKey, (void*&) pModel);
			hr = pModel->doRefresh();
			if(FAILED(hr))
				return hr; 
		}
	}


	return hr;
}

const BSTR g_sErrorMessageContainerId = ::SysAllocString(L"fpErrorMessageContainer");
void CXFormsXFormsElement::showError(const char * const  sError) 
{
	
	CComPtr<IHTMLElement> pErrorMessage;

	CComQIPtr<IHTMLDocument3> pDoc(m_pOwnerDocument);
	if(pDoc)
		pDoc->getElementById(g_sErrorMessageContainerId,&pErrorMessage);
	
	if(pErrorMessage)
	{
		CComBSTR bsError(sError);
		CErrorReportAction * pAction = m_ErrorActionFactory.CreateErrorReportAction(bsError,pErrorMessage);

		if(modalMessageActive())
		{
			AddPostModalAction(pAction);
		}
		else
		{
			pAction->Invoke();
			//Clear any used error reports
			m_ErrorActionFactory.Clear();
		}
	}
	else 
	{
		CComBSTR sFixedUpError(sError);
//		LegacyFixupErrorMessage(sError, &sFixedUpError);
//		LegacyMessage(sFixedUpError);
		::MessageBoxW(0,sFixedUpError,L"formsPlayer error",0);
		::AtlTrace("\n! 'formsPlayer.dll':\n Could not find proper error reporting receptacle.  check createErrorMessageReceptacle()");
	}
}


void CXFormsXFormsElement::createErrorMessageReceptacle()
{
	if(m_pOwnerDocument)
	{
		CComPtr<IHTMLElement> pBody;
		m_pOwnerDocument->get_body(&pBody);
		
		if(pBody)
		{
			CComBSTR sAfterBegin(L"afterBegin");
			CStringW sMessageElement;
			sMessageElement.Format(L"<%S:message id=\"%s\" level=\"modal\"></%S:message>",m_sDefaultNamespacePrefix,g_sErrorMessageContainerId,m_sDefaultNamespacePrefix);
			CComBSTR bs;
			bs.Attach(sMessageElement.AllocSysString());
			pBody->insertAdjacentHTML(sAfterBegin,bs);
		}
	}
}


void CXFormsXFormsElement::reportException(const char *sError,const long lErrorCode)
{
	//potentially log the error, but I can't be bothered.  
	//	Just report it to the user.
	// lErrorCode has a default value of 0 if you are too lazy to define
	//	a code
	if(sError)
	{
		showError(sError);
	}
	m_bFatalErrorEncountered = true;
}	

void CXFormsXFormsElement::reportError(const char *sError,const long lErrorCode) 
{
	//potentially log the error, but I can't be bothered.  
	//	Just report it to the user.
	// lErrorCode has a default value of 0 if you are too lazy to define
	//	a code

	if(sError)
	{
		showError(sError);
	}

}	

HRESULT CXFormsXFormsElement::LoadStylesheetCollectionIntoString(IHTMLStyleSheetsCollection * pColl) const
{
	//Loop through the stylesheets collection, serialising it into one
	//	big stylesheet.
	HRESULT hr = S_FALSE;

	long l, i;
	pColl->get_length(&l);
	CComVariant vtResult;
	for(i = 0; i < l;++i)
	{
		

		CComBSTR sTemp;
		_variant_t vtIx(i);
		pColl->item(&vtIx,&vtResult);
		CComQIPtr<IHTMLStyleSheet> pStyle = vtResult.pdispVal;
		if(!pStyle)
		{
			break;
		}

		CComPtr<IHTMLStyleSheetsCollection> pImports;
		pStyle->get_imports(&pImports);
		hr = LoadStylesheetCollectionIntoString(pImports);
		if(SUCCEEDED(hr))
		{
			CComPtr<IHTMLStyleSheetRulesCollection> pRules;
			hr = pStyle->get_rules(&pRules);
			if(SUCCEEDED(hr))
			{
				long l;
				pRules->get_length(&l);
				for(long i = 0; i < l; ++i)
				{
					CComPtr<IHTMLStyleSheetRule> pRule;
					hr = pRules->item(i,&pRule);
					if(SUCCEEDED(hr) && pRule)
					{
						CComBSTR sText;
						pRule->get_selectorText(&sText);
						if (sText.Length())
							sText.ToLower();
						if(!(sText == L"body") && !(sText == L"html"))
						{
							CComPtr<IHTMLRuleStyle> pStyle;
							hr = pRule->get_style(&pStyle);
							if(SUCCEEDED(hr) && pStyle)
							{
								CComBSTR sStyle;
								pStyle->get_cssText(&sStyle);
								sText.Append("{");
								sText += sStyle;
								sText.Append("} ");
								m_sCSS += sText;
							}
							else
							{
								break;
							}
						}
					}
					else
					{
						break;
					}
				}
			}
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::getCSS(BSTR *retVal,long * plLen) const
{
	//	definitely cache this, we should run through each rule
	//	and remove all body and html rules.  That could be 
	//	potentially processor intensive.

	//Disabled due to overstyling of viewlink body.
	//Reinstated for pseudoclass styling to work.
//#if 0
	HRESULT hr = E_FAIL;
	if(!m_sCSS)
	{

		CComPtr<IHTMLStyleSheetsCollection> pColl;
		hr = m_pOwnerDocument->get_styleSheets(&pColl);
		if(SUCCEEDED(hr))
		{
			m_sCSS = L" ";
			hr = LoadStylesheetCollectionIntoString(pColl);
			
			if(SUCCEEDED(hr))
			{
				m_sCSS.CopyTo(retVal);
				m_lCssLength = m_sCSS.Length();
				*plLen = m_lCssLength;
			}
			else
			{
				*retVal = 0;
				*plLen = 0;
			}
		}
	}
	else
	{
		*plLen = m_lCssLength;
		m_sCSS.CopyTo(retVal);
		hr = S_OK;
	}
	return hr;
}

void CXFormsXFormsElement::AddModel(CImplModel * pModel)
{
	//When a model is encountered in asynchronous processing,
	//	increase the pending model count, and add it to the 
	//	form's list of models.
	ASSERT(pModel != NULL);
	if(!m_pDefaultModel)
		m_pDefaultModel = pModel;
	if(!pModel->m_sId || !*pModel->m_sId)
	{
		CStringW wsId;
		wsId.Format(L"form<%X>::modelWithoutId<%X>",this,pModel);
		pModel->m_sId = wsId.GetString();
	}
	LPCTSTR sId = CW2A(pModel->m_sId);	
	LPVOID vd;
	if(sId  && !m_ModelList.Lookup(sId,vd))
	{
		m_ModelList.SetAt(sId, pModel);
		//if the model has been added late, (dynamically),
		//	UIInitialize won't be called in the normal cycle.
		//	it has to be done here.
		if(m_bDocumentInitialised)
		{
			pModel->UIInitialize();
		}
	}
	else
	{
		m_HeapPointers.AddHead(pModel);
		HRESULT hr = FP_E_DUPLICATE_MODEL;
		::SetupErrorInfo(hr,pModel->m_sId);
		reportException(hr);
	}
	++m_lPendingModels;
}

HRESULT CXFormsXFormsElement::AddSubmission(CImplSubmitInfo *pSubmitInfo)
{
	LPCTSTR sId = CW2A(pSubmitInfo->m_sId);	
/*	
	// I'VE COMMENTED THIS STUFF OUT BECAUSE THE WARNING WAS DOING MY
	// HEAD IN. FEEL FREE TO PUT IT BACK IF YOU ARE ACTUALLY GOING TO
	// USE IT. PHILBO - 20031028.
	LPVOID vd;
	//TEMP: submitinfos don't get deleted when they go out of focus
	//	find a way of sorting that out.
	if(true || !m_SubmitInfoList.Lookup(sId,vd))
	{
*/	
		//CMapStringToPtr doesn't like NULL, prefers empty string
		if(!sId)
			sId = "";
		m_SubmitInfoList.SetAt(sId, pSubmitInfo);
		
		//If this is added dynamically, it will need initialising
		if(m_bDocumentInitialised)
		{
			pSubmitInfo->initialize(false);
		}
/*	}
	else
	{
	//	m_HeapPointers.AddHead(pSubmitInfo);
		CString err;
		err.Format("Fatal Error: Duplicate submission-id \"%s\" encountered.",sId);
		reportException(err);
	}
*/	return S_OK;
}
void CXFormsXFormsElement::onDocumentComplete()
{
	if(!m_bDocumentComplete)
	{
		//this prevents further calls.	
		m_bDocumentComplete = true;

		if(!m_pCSSHandler)
			addDefaultStylesheet();

		
		//Insert a receptacle for error messages reported by formsPlayer.
		//Cannot be called "on demand",  as that leaves  too little time to 
		//	initialise the message before wishing to display it.
		createErrorMessageReceptacle();
		//check for foreign mustunderstands
		bool b;
		HRESULT hr = checkMustUnderstand(&b);
		if(FAILED(hr) || !b)
			m_bFatalErrorEncountered = true;


		if(m_lPendingModels == 0)
		{
			POSITION pos = m_ModelList.GetStartPosition();
			CString string;
			while (pos != NULL && !m_bShutDown)
			{
				CImplModel* pImpl = NULL;
				m_ModelList.GetNextAssoc(pos, string, (void*&) pImpl);
				// HACK so that 
				pImpl->rebuild(false);
				
				pImpl->ready();
			}
		}
		//this should be called when the document has loaded and the pendingModel
		//	count is at 0.  

		if(canContinue())
		{
			HRESULT hr = S_OK;	
		
			/*
					4.2 Initialization Events
						This section defines the various stages of the
						initialization phase.  The processing application
						begins initialization by dispatching an event 
						xforms:modelConstruct to each XForms Model in 
						the containing document.
			 */
			

			/*
				4.2.2 xforms:modelInitialize
					5.	After all XForms Models are initialized, 
						the host must dispatch an xforms:UIInitialize 
						event to each model element.
			 */

			//Orphans are controls that are not bound to any model,
			// such as captions that have inline content.
			//	these can be initialised either before or after 
			//	UIInitialize is called by the models.
			//If UIInitialize becomes asynchronous, then after is 
			//	the correct place, as we can kick off UIInitiliaze
			//	then run this while we are waiting.
			
			{
				// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
				POSITION pos = m_Orphans.GetStartPosition();
				CFormControl * pFC = NULL;
				while(pos)
				{
					m_Orphans.GetNextAssoc(pos, pFC, pFC);
					hr = pFC->initialize(false);
					//if the display failed, we may wish to try again later.
					if(SUCCEEDED(hr))
					{
						m_UndisplaidOrphans.RemoveKey(pFC);
					}
					hr = S_OK;
				}
			}

			if (SUCCEEDED(hr) && canContinue())
			{
				//loop through the models, calling UIInitialize on each.
				CString key;
				CImplModel * pModel = NULL;
				POSITION pos = m_ModelList.GetStartPosition();
				while (pos != NULL)
				{
					m_ModelList.GetNextAssoc(pos, key, (void*&) pModel);

					hr = pModel->UIInitialize();
					
					//TODO: punishment for failure should be harsher.
					if (FAILED(hr))
						break;
				}
			}
			FocusControlAwaitingFocus();
			DecreaseDeferredUpdateCnt();
			m_bDocumentInitialised = true;


		}
		else
		{
			AtlTrace("'formsplayer.dll': DocumentComplete called after fatal error - form %X \n",this);
		}
//	brandForm();
	}
	else
	{
		AtlTrace("'formsplayer.dll': DocumentComplete called multiple times - form %X (normal)\n" , this);
	}

}

HRESULT CXFormsXFormsElement::GetFactoryId(CComBSTR & sId)
{
	HRESULT hr = E_FAIL;
	CComQIPtr<IHTMLDocument3> pDoc3(m_pOwnerDocument);
	if(pDoc3)
	{
		CComBSTR sObject("object");
		CComPtr<IHTMLElementCollection> pColl;
		pDoc3->getElementsByTagName(sObject,&pColl);
		if(pColl)
		{
			long len;
			pColl->get_length(&len);		
			CComVariant vtZero(0l);
			//loop through the object elements
			for(long l = 0;l < len; ++l)
			{
				CComPtr<IDispatch> pDisp;
				pColl->item(CComVariant(l),vtZero,&pDisp);
				if(pDisp)
				{
					//Get the contained object out of the object element
					CComQIPtr<IHTMLObjectElement> pObj(pDisp);
					if(pObj)
					{
						CComPtr<IDispatch> pDispObj;
						pObj->get_object(&pDispObj);
						if(pDispObj)
						{
							//Check that it is an XForms factory
							CComQIPtr<IFactory> pFactory(pDispObj);
							if(pFactory)
							{
								CComQIPtr<IHTMLElement> pEl(pDisp);
								pEl->get_id(&sId);
								break;
							}
						}
					}
				}

			}
		}
	}
	return hr;

}

HRESULT CXFormsXFormsElement::serializeNamespaces()
{
	//extracts the namespaces property of the document to a string
	//	compatible with xml namespace declarations
	HRESULT hr = S_OK;
	
	CComQIPtr<IHTMLDocument4> pDoc4 = m_pOwnerDocument;
	if(pDoc4) {
		CComPtr<IDispatch> pDisp = NULL;
		pDoc4->get_namespaces(&pDisp);
	
		CComQIPtr<IHTMLNamespaceCollection> pColl = pDisp;

		if(pColl) {
			long lLen;
			pColl->get_length(&lLen);
			//set the namespace prefix for internally used containers.
			m_sNamespaceDeclaration = " xmlns:fp0917='urn:formsplayer.com' xmlns:fpUI='urn:formsplayer.com'";

			CString sXFImportDirectiveTempl("<?import namespace='%S' implementation='#idb_XForms'?>");
			CString sDirective;
			CComQIPtr<IHTMLNamespace> pNS;
			CComBSTR sFactoryId;
			GetFactoryId(sFactoryId);
			CComBSTR sFactoryURL = L"#";
			sFactoryURL += sFactoryId;

			for(long i = 0;i < lLen;++i)
			{
				CComPtr<IDispatch> pDisp;
				CComBSTR sThisDecl("");
				pColl->item(_variant_t(i),&pDisp);
				pNS = pDisp;
				if(pNS) {
					CComBSTR sName;
					pNS->get_name(&sName);
					sThisDecl += " xmlns:";
					sThisDecl += sName;
					sThisDecl += "='";
					//Only add it if it is not a duplicate.
					if(!wcsstr(m_sNamespaceDeclaration,sThisDecl))
					{
						CComBSTR sURN;
						CComVariant vtR;
						pNS->get_readyState(&vtR);
						pNS->get_urn(&sURN);
						if(sURN.m_str)
						{
							m_sNamespaceDeclaration	+= sThisDecl;
							m_sNamespaceDeclaration += sURN;
							m_sNamespaceDeclaration += "'";
							//Save it to create import directives.
							if(sURN == theApp.m_sXFormsURI || sURN == theApp.m_sXFormsURI_NovellHack /* || sURN == theApp.m_sOldXFormsURI */)
							{	
								if(m_sDefaultNamespacePrefix.IsEmpty())
									m_sDefaultNamespacePrefix = sName.m_str;
								sDirective.Format(sXFImportDirectiveTempl,sName);
								m_sImportDirectives.Append(sDirective);
								HRESULT _hr = pNS->doImport(sFactoryURL);
								m_lstNamespacePrefixesWhichCorrespondToTheXFormsNamespace.push_front(sName);

							}
							//Don't warn the user, it's annoying
							if(false && sURN == theApp.m_sOldXFormsURI)
							{
								//Warn the user.
								CString s;
								s.Format("This form uses an out-of date URI for the XForms namespace.<br />'%S' should be '%S'",sURN,theApp.m_sXFormsURI);
								
								reportError(s);
							}
						}
					}
				}
			}
			//cache the length of this declaration.
			m_lNamespaceDeclaration = m_sNamespaceDeclaration.Length();
			if(m_sDefaultNamespacePrefix.IsEmpty())
			{
				//Should be error!? Yes!
				hr = FP_E_NO_NAMESPACE;
				CComBSTR sNS;
				sNS.LoadString(IDS_XFORMS_URI);
				::SetupErrorInfo(hr,sNS);
				reportException(hr);

			}
			
			//cache the length of the import directive string.
			m_lImportDirectives = m_sImportDirectives.GetLength();
		}
	}
	return hr;
}

void CXFormsXFormsElement::onCloseDialog(CMessageDialog *pDlg)
{
	POSITION pos = m_OpenMessageWindows.Find(pDlg);
	if(pos)
	{
		m_OpenMessageWindows.RemoveAt(pos);
		m_ClosingMessageWindows.AddTail(pDlg);
	}
}

HRESULT CXFormsXFormsElement::RemoveFormControl(CFormControl *pControl)
{
	//returns: 
	//	S_FALSE nothing to delete,
	//	S_OK, successfully deleted.
	HRESULT hr = S_FALSE;
	// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
	m_UndisplaidOrphans.RemoveKey(pControl);
	
	if(m_Orphans.RemoveKey(pControl))
		hr = S_OK;

	return hr;
}


//m_bFatalException implemented through functions
//	for easier tracking.
// also, because it is write-once, we don't want other
//	objects to be able to set it to false once it is true.

//TODO: The follolwing functions should check if the ID is a duplicate
// and return E_FAIL if it is.  The problem with this is that any elements 
//	with an ID in a repeat will return an error, we need to generate an id
//	based on the repeat prefix.
const CComBSTR g_bsEXSLTDynamicNS(L"http://exslt.org/dynamic");
const CComBSTR g_bsFPNS(L"http://www.formsplayer.com/extensionfunctions");

//	could be inlined?

HRESULT CXFormsXFormsElement::getFunctionLibrary(BSTR sURI, IDispatch **ppDisp) const
{
	HRESULT hr = S_FALSE;
	if(sURI &&  g_bsEXSLTDynamicNS != sURI && g_bsFPNS != sURI)
	{
		*ppDisp=NULL;

		CComBSTR sXPath(L"/config/library[@URI=\"");
		sXPath += sURI;
		sXPath.Append("\"]");

		CComPtr<IXMLDOMNode> pNode;
		m_Config.m_pXMLParser->selectSingleNode(sXPath,&pNode);
		if(pNode)
		{
			CComBSTR sCLSID;
			hr = pNode->get_text(&sCLSID);
			if(SUCCEEDED(hr))
			{
				IDispatchPtr p;
				hr = p.CreateInstance(sCLSID);
				if(SUCCEEDED(hr))
				{
					p.AddRef();
					(*ppDisp) = p;
				}
			}

		}


			
		/*

		//build the expression to find the CLSID in the config document.
		CComBSTR sXPath(L"/config/library[@URI=\"");
		sXPath += sURI;
		sXPath.Append("\"]/text()");


		//This is too slow, and  we don't need the advanced XPath features that it gives.
		CComPtr<IXPathExpression> pExpr;
		hr = theApp.m_pEvaluator->createExpression(sXPath,m_pNSResolver,&pExpr);
		if(SUCCEEDED(hr))
		{
			CComBSTR sCLSID;
			CComPtr<IXPathResult> pResult;
			//execute the expression.
			hr = pExpr->evaluate(m_Config.m_pXMLParser, STRING_TYPE, NULL, &pResult);
			if(SUCCEEDED(hr) && pResult)
			{
				hr = pResult->get_stringValue(&sCLSID);	
				if(SUCCEEDED(hr))
				{
					IDispatchPtr p;
					hr = p.CreateInstance(sCLSID);
					if(SUCCEEDED(hr))
					{
						p.AddRef();
						(*ppDisp) = p;
					}
				}
			}
		}
		*/
	}
	else if(m_pFuncLib)
	{
		//Return the default library.
		hr = m_pFuncLib->QueryInterface(IID_IDispatch,(void**)ppDisp);
	}
	else
	{
		*ppDisp = 0;
		hr = E_PENDING;
	}
	return hr;
}

void CXFormsXFormsElement::onChangeFocus(CFormControl * pControl)
{
	if(pControl == m_pCurrentControl)
	{
		//no change, do nothing.
	}
	else
	{
		m_pPreviousControl = m_pCurrentControl;
		m_pCurrentControl = pControl;
	}
}

//could be void.
HRESULT CXFormsXFormsElement::modalMessage(const BSTR sText,bool bNavigate) const
{
	//no need to add to a message list, as its lisfetime
	//	is governed by its modality
	CMessageDialog oMessage(sText,bNavigate,true,NULL);
//	oMessage.DoModal();
	
	return S_OK;
}


HRESULT CXFormsXFormsElement::AddInstance(CImplInstance * pInstance)
{
	HRESULT hr = S_FALSE;
	LPCTSTR sId = CW2A(pInstance->m_sId);	
	if(sId && sId[0])
	{
		void * pvOldInstance = 0;
		if(m_Instances.Lookup(sId,pvOldInstance))
		{
			//Instance Id already taken.
			hr = FP_E_DUPLICATE_INSTANCE;
			::SetupErrorInfo(hr,pInstance->m_sId);
			reportException(hr);
		}
		else
		{
			m_Instances.SetAt(sId,pInstance);
			hr = S_OK;
		}
	}
	
	return hr;
}

bool CXFormsXFormsElement::StopAddingControls()
{
	//finish calling init on any elements that couldn't
	//	be fully done at load-time
	HRESULT hr = S_OK;
	bool bRet = false;
	if(m_bAmAddingControls && m_lPendingElements == 0)
	{
		m_bAmAddingControls = false;
		if(!m_pDefaultModel)
		{
			CComPtr<IHTMLElement> pEl;
			m_pOwnerDocument->get_body(&pEl);
			if(pEl)
			{
				CComBSTR sElement(L"<");
				sElement.Append(m_sDefaultNamespacePrefix);
				sElement += L":model />";
				CComBSTR sPosition(L"beforeEnd");
				pEl->insertAdjacentHTML(sPosition,sElement);
			}
		}
		else
		{
			POSITION  pos  = m_lstInitPending.GetHeadPosition();
			while(pos && canContinue())
			{
				POSITION pos2 = pos;
				CImplNodeBinding * pControl = m_lstInitPending.GetNext(pos);
				hr = pControl->CImplNodeBinding::init(NULL);
				if(FAILED(hr))
				{
					m_lstInitPending.RemoveAt(pos2);
					delete pControl;
				}
			}

			//if the document is already initialised, properly initialise
			//	any new controls
			if(isDocumentComplete() && m_bDocumentInitialised)
			{
				while(!m_lstInitPending.IsEmpty())
				{
					CImplNodeBinding * pControl = m_lstInitPending.RemoveHead();
					//TEMP: CImplModel is cast to formcontrol in m_pImpl
					// make sure that this is an actual Form control.
					if(pControl)
						hr = pControl->initialize(false);
					if(FAILED(hr))
					{
						delete pControl;
					}
				}
			}
			else
			{
				//If the document is not yet complete, any uninitialised controls
				//	will be initialised properly by their model, or the orphan list.
				m_lstInitPending.RemoveAll();
			}

	//		refresh();
			bRet =  true;
		}
	}
	return bRet;
}

STDMETHODIMP CXFormsXFormsElement::submitInstance(BSTR sId)
{
	HRESULT hr = S_OK;
	CImplSubmitInfo* pSubmitInfo = NULL;

	hr = getSubmitInfo(sId, pSubmitInfo);
	if (S_OK == hr)
		hr = pSubmitInfo->onactivate();
	return hr;
}

HRESULT CXFormsXFormsElement::Remove(const BSTR sId)
{
	LPCTSTR sKey = CW2A(sId);
	if(m_Instances.RemoveKey(sKey))
	{}
	else if(m_Binds.RemoveKey(sKey))
	{}
	else if(m_SubmitInfoList.RemoveKey(sKey)) 
	{}
	return S_OK;
}

void CXFormsXFormsElement::RemoveModel(CImplModel *pMod)
{
	CString sKey(pMod->m_sId);
	m_ModelList.RemoveKey(sKey);
}

HRESULT CXFormsXFormsElement::GetNSResolver(IXPathNSResolver **pResolver)
{
	HRESULT hr;
	if(m_pNSResolver)
	{
		*pResolver = m_pNSResolver;
		(*pResolver)->AddRef();
		hr = S_OK;
	}
	else
	{
		hr = FP_E_NO_NSRESOLVER;
		::SetupErrorInfo(hr,0);
		//The exception is reported here, as we do not want to rely on 
		//	the caller reporting it (thus setting the fatal flag).
		//We could set the fatal flag here and let the caller 
		//	choose to report the exception to the user,
		//	but for now I would like to keep access to 
		//	that flag restricted.
		reportException(hr);
		*pResolver = 0;
	}
	return hr;
}

void CXFormsXFormsElement::RegisterControlContainer(CImpl *pContainer, IUnknown *pKey)
{
	//Make sure we really have the pointer to the IUnknown 
	//  interface, not just a cast or implementor.
	//	Handily, this also performs an addref.
	IUnknown * pRealKey = 0;
	pKey->QueryInterface(IID_IUnknown,(void**)&pRealKey);
	//Insert this pointer in to the map.
	m_ControlContainers.SetAt(pRealKey,pContainer);
}

HRESULT CXFormsXFormsElement::GetParentImpl(IUnknown *pKey, CImpl **ppParent)
{
	HRESULT hr = E_FAIL;
	//Ensure that the IUnknown ptr is the real deal, and not just a C-cast or something.
	IUnknown * pRealKey = 0;
	hr = pKey->QueryInterface(IID_IUnknown,(void**)&pRealKey);
	if(pRealKey)
	{
		CImpl * pVal = 0;
		if(m_ControlContainers.Lookup(pRealKey,(void*&)pVal))
		{
			*ppParent = pVal;
			hr = S_OK;
		}
		else
		{
			*ppParent = 0;
			hr = S_FALSE;
		}
		::safeRelease(pRealKey);
	}
	return hr;
}

void CXFormsXFormsElement::DeregisterControlContainer(IUnknown *pUnk)
{
	if(pUnk)
	{
		if(m_ControlContainers.RemoveKey(pUnk))
			pUnk->Release(); //not safeRelease
	}
}

void CXFormsXFormsElement::showSplash(void) 
{
	if(m_XFConfig.SplashPage()) {  // Bug 372: Allow splash page to be disabled.
		CComBSTR bsPath;
		bsPath.Attach(m_XFConfig.SplashPageURL());
		bsPath.ToLower();
		if(bsPath != theApp.m_bsLastSplash) {  // Bug 310: Display on a per-splash basis.
			if(m_pSplash) {
				delete m_pSplash;
				m_pSplash = 0;
			}
			m_pSplash = new CMessageDialog(bsPath, true, false, this, 8000);
			theApp.m_bsLastSplash.Empty();
			theApp.m_bsLastSplash.Attach(bsPath.Detach());
		}
	} else
		theApp.m_bsLastSplash.Empty();
}

// Insert the new-style branding in the form.
void CXFormsXFormsElement::NeoBrandForm(void)
{
	// Check that we should be branding the form.
	if(m_XFConfig.FormBranding()) {
		// Bug 843: Invoke a Google AdSense toolbar as well as branding the form.
		//theApp.FormsManager().InvokeSoftbar(this, CComBSTR(L"{6E398F38-B49F-4EAF-80DE-AD5345F4DB42}"), true);
	
		if(!m_bBranded && m_pOwnerDocument) {
			CComPtr<IHTMLElement> pBodyEl;
			// Get the HTML <body> element.
			m_pOwnerDocument->get_body(&pBodyEl);
			if(pBodyEl) {
				// Get the style for the <body>.
				CComPtr<IHTMLStyle> pStyle;
				pBodyEl->get_style(&pStyle);
				if(pStyle) {
					// Get the location of our branding image.
					char buf[MAX_PATH];
					DWORD dw = ::GetModuleFileName(_Module.m_hInst, buf, MAX_PATH);
					buf[dw] = '\0';

					CStringW sBrandingImage;
					sBrandingImage.Format(L"url(\"res://%S/fpvert.gif\")", buf);

					// Alter the style to display our branding.
					CComVariant vNewSetting = L"22px";
					pStyle->put_marginLeft(vNewSetting);

					vNewSetting = L"#FFFFFF";
					pStyle->put_backgroundColor(vNewSetting);

					CComBSTR sNewSetting = L"fixed";
					pStyle->put_backgroundAttachment(sNewSetting);

					sNewSetting = L"no-repeat";
					pStyle->put_backgroundRepeat(sNewSetting);

					sNewSetting = L"top left";
					pStyle->put_backgroundPosition(sNewSetting);

					sNewSetting = sBrandingImage;
					pStyle->put_backgroundImage(sNewSetting);

					m_bBranded = true;
				}
			}
		}
	}
}

// Remove the new-style branding from the form.
void CXFormsXFormsElement::NeoDeBrandForm(void)
{
	//if(!m_XFConfig.FormBranding())
	//	theApp.FormsManager().RevokeSoftbar(this, CComBSTR(L"{6E398F38-B49F-4EAF-80DE-AD5345F4DB42}"), true);
}

// Invoke the Soft-SysTray instance for this form.
/*
HRESULT CXFormsXFormsElement::InvokeDefaultSystray(void)
{
	HRESULT hr = S_OK;
	// Only continue if the Soft-SysTray has not already been invoked on this form.
	if(!m_bSystrayInvoked && m_pOwnerDocument) {
		// Bug 1141: Load the Soft-SysTray settings from the HTML DOM instead of reloading the document as XML.
		CComBSTR sTitle, sFormTitle;
		// Get the title.
		m_pOwnerDocument->get_title(&sFormTitle);
		if(sFormTitle.Length() <= 0) {
			if(m_XFConfig.FormBranding() || m_XFConfig.ControlBranding()) {
				// Brand the title.
				sTitle = L"fP: ";
				sTitle += sFormTitle;
			} else
				sTitle = sFormTitle;
		} else
			sTitle = L"formsPlayer";

		// Get the a collection of all the <link> elements.
		CComPtr<IHTMLElementCollection> pLinkElements;
		m_pOwnerDocument->get_links(&pLinkElements);
		if(pLinkElements) {
			CComBSTR sIcon, sMenu;
			bool bDisplaySysTrayIcon = false, bProcessedIcon = false, bProcessedMenu = false;
			long nLinkElements;
			pLinkElements->get_length(&nLinkElements);
			VARIANT vIndex, vSubIndex;
			vIndex.vt = vSubIndex.vt = VT_I4;
			vSubIndex.intVal = 0;
			// Loop through each one.
			for(vIndex.intVal = 0; vIndex.intVal < nLinkElements && (bProcessedIcon == false || bProcessedMenu == false); vIndex.intVal++) {
				IDispatch *pDisp = 0;
				pLinkElements->item(vIndex, vSubIndex, &pDisp);
				if(pDisp) {
					CComQIPtr<IHTMLLinkElement> pLinkElement = pDisp;
					if(pLinkElement) {
						CComBSTR sRel;
						pLinkElement->get_rel(&sRel);
						sRel.ToLower();
						if(bProcessedIcon == false && sRel == L"icon") {
							// Get the icon.
							pLinkElement->get_href(&sIcon);
							bProcessedIcon = true;
							bDisplaySysTrayIcon = true;
						} else if(bProcessedMenu == false && sRel == L"menu")  {
							// Get the menu.
							pLinkElement->get_href(&sMenu);
							bProcessedMenu = true;
							if(sMenu.Length() <= 0) {
								// Set a default menu if not specified.
								sMenu = L"res://";
								char buf[MAX_PATH + 1];
								DWORD dw = ::GetModuleFileName(_Module.m_hInst, buf, MAX_PATH);
								buf[dw] = L'\0';
								sMenu += CA2W(buf);
								sMenu += L"/dfltstry.htm";
							}
						}
					}
					pDisp->Release();
				}
			}

			if(bDisplaySysTrayIcon) {
				// Clean up any existing system tray icon.
				if(m_pDefaultSysTrayIcon)
					m_pDefaultSysTrayIcon.Release();

				// Invoke the Soft-SysTray.
				hr = theApp.FormsManager().InvokeSystrayStrict(this, sMenu, sTitle, sIcon, 236, 200, true, CComBSTR(L"ne"), 200, &m_pDefaultSysTrayIcon);
			}
		}
	} else
		hr = S_FALSE;

	return hr;
}

// Return a pointer to the Soft-SysTray instance for this form.
ISysTrayIcon *CXFormsXFormsElement::DefaultSystray(void)
{
	ISysTrayIcon *pRetval = 0;
	if(m_pDefaultSysTrayIcon)
		pRetval = m_pDefaultSysTrayIcon.p;
	else
		pRetval = theApp.FormsManager().DefaultSystray();
	return pRetval;
}
*/
HRESULT CXFormsXFormsElement::AddInlineSchema(BSTR bsSchemaId, BSTR sSchemaText,IXMLDOMDocument** ppSchemaDoc)
{
	CDOM3Document doc;
	VARIANT_BOOL b;
	HRESULT hr = doc.m_pXMLParser->loadXML(sSchemaText,&b);
	if(b)
	{
		LPCTSTR sSchemaId = CW2A(bsSchemaId);	
		if(sSchemaId && sSchemaId[0])
		{
			LPDISPATCH pDisp;
			if(m_mapInlineSchemata.Lookup(sSchemaId,(void*&)pDisp))
			{
				::safeRelease(pDisp);
			}
			IXMLDOMDocument2 * pDoc2 = 0;
			doc.m_pXMLParser->QueryInterface(IID_IXMLDOMDocument2, (void**)&pDoc2);
			if(pDoc2)
				m_mapInlineSchemata.SetAt(sSchemaId,pDoc2);
			hr = S_OK;
			
		}
		else
		{
			hr = S_FALSE;
		}

		if(ppSchemaDoc)
		{
			//If a return has been requested, fill it.
			*ppSchemaDoc = doc.m_pXMLParser;
			(*ppSchemaDoc)->AddRef();
		}

	}
	else
	{
		CComPtr<IXMLDOMParseError> pPE;
		doc.m_pXMLParser->get_parseError(&pPE);
		
		
		CComBSTR bsReason,bsSrc;

		pPE->get_reason(&bsReason);
		pPE->get_srcText(&bsSrc);
		hr = FP_E_BADLYFORMED_SCHEMA;
		theApp.FormsManager().GetErrorSource().SetupErrorInfo(hr,bsSchemaId,0,bsReason,bsSrc);
		//reporting now to ensure that fatal flag is set.
		reportException(hr);
	}
	return hr;

}

HRESULT CXFormsXFormsElement::GetInlineSchema(BSTR bsURL, IXMLDOMDocument2 **ppDoc)
{
	LPCTSTR sURL = CW2A(bsURL);	
	HRESULT hr = E_FAIL;
	if(m_mapInlineSchemata.Lookup(sURL,(void*&)*ppDoc))
	{
		(*ppDoc)->AddRef();
		hr =  S_OK;
	}
	else
	{
		CComQIPtr<IHTMLDocument3> pDoc3(m_pOwnerDocument);
		if(pDoc3)
		{
			CComPtr<IHTMLElement> pEl;
			hr = pDoc3->getElementById(bsURL,&pEl);
			if(pEl)
			{
				CComBSTR sOuterHTML;
				hr = pEl->get_outerHTML(&sOuterHTML);
				if(sOuterHTML)
				{
					BSTR s = sOuterHTML.m_str;
					if(sOuterHTML[1]==L'?')
					{
						//IE has added its stupid processing instruction.
						s = &sOuterHTML[1];
						while(*s && *s!=L'<')
						{
							++s;
						}
						//And it has stripped out the quotes on  id, and any other attrs that it recognises.
						wchar_t * sBar = s;
						//shunt to the first attribute,
						//	if no attributes exist, shunt to just before end of 
						//	element opening tag
						while(*sBar && sBar[1] != '>' && *sBar != ' ')
						{
							++sBar;
						}
						//Run through the opening tag, fixing any attribute declarations.
						++sBar;
						while(*sBar && *sBar != '>')
						{
							if(*sBar == '=')
							{
								//The top level id is no longer of interest to us.
								//	If HTML has stripped quotes from any other 
								//	attributes, then this might cause a problem
								//	it won't affect any that have not had their 
								//	quotes stripped.
								sBar[1] = '\"';
							}
								
							if(*sBar == ' ')
							{
								//As above, but this will cause trouble in an
								//	attribute which contains a space.
								//	C'est la vie.
								sBar[-1] = '"';
							}
							++sBar;
						}
					}
					//add the schema to the list so we don't have to do all this again,
					// then return
					CComPtr<IXMLDOMDocument> pSchemaDoc;
					hr = AddInlineSchema(bsURL, s,&pSchemaDoc);
					
					if(pSchemaDoc)
					{
						hr = pSchemaDoc->QueryInterface(IID_IXMLDOMDocument2,reinterpret_cast<void**>(ppDoc));
					}
				}
			}
		}
	}
	return hr;
}

void CXFormsXFormsElement::deleteControl(CFormControl *pControl)
{
	//Places control into the toDelete list.
	//	Cares not for whether it is in the
	//	live list or not.
	m_DeadFormControls.AddHead(pControl);
}

HRESULT CXFormsXFormsElement::burnDeadControls()
{
	HRESULT hr = S_OK;
	while(!m_DeadFormControls.IsEmpty())
	{
		CFormControl * pFc = m_DeadFormControls.RemoveHead();
		// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance			
		if(m_Orphans.RemoveKey(pFc))
		{
			m_UndisplaidOrphans.RemoveKey(pFc);

			pFc->RequestDeleteAtEarliestConvenience();
		}
	}

	return hr;
}


HRESULT CXFormsXFormsElement::GetHostDocument(IHTMLDocument4 ** ppDoc)
{
	//TODO: once interface has been defined, implement the viewHost 
	//		portion of this function

	HRESULT hr = E_FAIL;
	//Nullify the ppDoc so that in the event of method 1 failing, 
	//	method 2 can take over.
	*ppDoc = 0;
	CComPtr<IHTMLWindow2> pWin;
	hr = m_pOwnerDocument->get_parentWindow(&pWin);
	CComPtr<IDispatch> pDisp;
	if(pWin)
		pWin->get_external(&pDisp);
	if(pDisp)
	{
		HRESULT hr;
		LPOLESTR sName = L"doc4";
		DISPID dispId;
		hr = pDisp->GetIDsOfNames(IID_IDispatch,&sName,1,LOCALE_SYSTEM_DEFAULT,&dispId);
		if(SUCCEEDED(hr) && dispId)
		{
			CComVariant vt;
			DISPPARAMS params;

			hr = pDisp->Invoke
			(
				dispId,
				IID_IDispatch,
				LOCALE_SYSTEM_DEFAULT,
				DISPATCH_PROPERTYGET,
				&params,
				&vt,
				0,
				0
			);

			if(vt.vt == VT_DISPATCH && vt.pdispVal)
			{
				hr = vt.pdispVal->QueryInterface(IID_IHTMLDocument4,(void**)ppDoc);
			}
		}
	}

	
	if(!*ppDoc)
	{
		hr = m_pOwnerDocument->QueryInterface(IID_IHTMLDocument4,(void**)ppDoc);
	}
	//::safeRelease(pVH);

	return hr;
}

// Displays any orphans that are pending initialisation.
HRESULT CXFormsXFormsElement::showNewOrphans(void)
{
	HRESULT hr = S_FALSE;

	if(!m_UndisplaidOrphans.IsEmpty())
	{
		//If there are orphans that have not been displayed,
		//	Try to refresh them.
		// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
		POSITION pos = m_UndisplaidOrphans.GetStartPosition();
		CFormControl * pFC = 0;
		CFormControl * pKeyFC = 0;
		while(pos)
		{
			m_UndisplaidOrphans.GetNextAssoc(pos, pKeyFC, pFC);
			hr = pFC->refresh();
			if(m_UndisplaidOrphans.IsEmpty())
			{
				//the form has closed in the meantime, and emptied this list.
				pos = 0;
			}
			//Not SUCCEEDED(hr), refresh returns S_FALSE when it cannot yet
			//	do the actual refreshing.
			else if(S_OK == hr)
			{
				m_UndisplaidOrphans.RemoveKey(pKeyFC);
			}
		}
		hr = S_OK;
	}
//Not orphans
	if(m_bDocumentInitialised)
	{
		//Initialize any controls that have not yet been done
		CImplModel* pModel = 0;
		CString sKey;
		bool bPending;
		POSITION pos;
pendingLoop:
		bPending = false;
		pos =m_ModelList.GetStartPosition();
		while(pos)
		{
			m_ModelList.GetNextAssoc(pos,sKey, (void*&) pModel);
			bool bPending0 = pModel->initializePendingControls();
			if(!bPending)
				bPending = bPending0;
		}
		//There have been controls initialized in this runthrough,
		//	do another run.
		if(bPending)
		{
			goto pendingLoop;
		}
	}

	return hr;
}

void CXFormsXFormsElement::insertResourceLocation(CStringW & sFormatString, bool bEscapeSlashes)
{
	// Get DLL path and prefix it with the res protocol
	char fullpath[MAX_PATH + 6] = "res://";
	::GetModuleFileName(_Module.m_hInst,&fullpath[6],MAX_PATH);
	
	// Have found that css treats the backslash as the escape character
	// well it dones anyway on the xsol datetime control. 
	CStringW sPath(fullpath);
	if(bEscapeSlashes)
		sPath.Replace(L"\\",L"\\\\");
	//TODO: URLEncode properly
	sPath.Replace(L" ",L"%20");

	// Replace all %FORMSPLAYER_PATH% within the UI to the 
	// protocol + path combination so that the resources can be loaded 
	// from the dll.
	sFormatString.Replace(L"%FORMSPLAYER_PATH%", sPath);
}

// <summary>checks if there are any mustunderstand attributes on elements that we don't understand</summary><param name="pbAllUnderstood">pointer to a bool which recieves false if there are any elements that we don't (and must) understand, and true if there are none. </param>
HRESULT CXFormsXFormsElement::checkMustUnderstand(bool * pbAllUnderstood)
{
	HRESULT hr = E_FAIL;
	*pbAllUnderstood = true;
	//create the attribute name to search for.
	//TODO: this currently only uses the first found
	//	xforms namespace prefix, should be on URN
	//	but that is not possible in HTML
	//	so either get the document as XML, or 
	//	use a list of potential attribute names.
	CComBSTR sMustUnderstand(m_sDefaultNamespacePrefix);
	sMustUnderstand += ":mustUnderstand";
	
	CComPtr<IHTMLElementCollection> pColl;
	m_pOwnerDocument->get_all(&pColl);

	if(pColl)
	{
		CComPtr<IUnknown> pUnk;
		pColl->get__newEnum(&pUnk);
		if(pUnk)
		{
			CComQIPtr<IEnumVARIANT> pEnum = pUnk;
			if(pEnum)
			{
				hr = S_OK;
				CComVariant vEl;
				while(S_OK == pEnum->Next(1,&vEl,0))
				{
					CComQIPtr<IHTMLElement> pEl = vEl.pdispVal;
					
					CComVariant vtVal;
					hr = pEl->getAttribute(sMustUnderstand,3,&vtVal);
					bool bMustUnderstand = false;
					if(vtVal.vt == VT_BSTR)
					{
						if(0 == wcscmp(vtVal.bstrVal,L"true"))	
						{
							bMustUnderstand = true;
						}
					}
					else if(vtVal.vt == VT_BOOL)
					{
						if(vtVal.boolVal)
						{
							bMustUnderstand = true;
						}
					}

					if(bMustUnderstand)
					{
						CComBSTR sTagName;
						pEl->get_tagName(&sTagName);
						CComBSTR sNS;
						CComQIPtr<IHTMLElement2>(pEl)->get_tagUrn(&sNS);
						BOOL bCanUnderstand;
						theApp.FormsManager().canUnderstandTag(sNS,sTagName,&bCanUnderstand);
						if(!bCanUnderstand)
						{

							hr = FP_E_UNKNOWN_ELEMENT;
							::SetupErrorInfo(hr,sTagName);
							reportException(hr);
							*pbAllUnderstood = false;
							break;
						}
					}

					vEl.Clear();
				}

			}
		}
	}
	return hr;
}


void CXFormsXFormsElement::modelConstructDone(CImplModel * pModel)
{
	
	if(--m_lPendingModels == 0 && m_bDocumentComplete)
	{
		POSITION pos = m_ModelList.GetStartPosition();
		CString string;
		while (pos != NULL)
		{
			CImplModel* pImpl = NULL;
			m_ModelList.GetNextAssoc(pos, string, (void*&) pImpl);
			pImpl->ready();
		}
	}

}

// adds a reference linking an XML document to a model into the approproate list.
void CXFormsXFormsElement::SetModelDocument(CImplModel * pModel , IUnknown * pUnkDocument)
{
	m_MapDocToModel.SetAt((void*)pUnkDocument, (void*) pModel);
}

void CXFormsXFormsElement::RemoveModelDocument(IUnknown * pUnkDocument)
{
	// Remove the specified document from the document modal map.
	m_MapDocToModel.RemoveKey((void *) pUnkDocument);
}

// Superceded by bug 267...
//bool CXFormsXFormsElement::CheckRegistryForBrandingKeys(void)
//{
//	HKEY h;
//	bool rc = true;
//	if(RegOpenKeyEx(HKEY_CURRENT_USER, g_lpszBrandingKey1, 0, KEY_READ, &h) == ERROR_SUCCESS) {
//		DWORD dw = 39;
//		char buf[39];
//		if(RegQueryValueEx(h, g_lpszBrandingValueName1, 0, 0, (LPBYTE)buf, &dw) == ERROR_SUCCESS) {
//			RegCloseKey(h);
//			if(RegOpenKeyEx(HKEY_CURRENT_USER, g_lpszBrandingKey2, 0, KEY_READ, &h) == ERROR_SUCCESS) {
//				char buf2[39];
//				if(RegQueryValueEx(h, g_lpszBrandingValueName2, 0, 0, (LPBYTE)buf2, &dw) == ERROR_SUCCESS) {
//					RegCloseKey(h);
//					if(strcmp(buf, g_lpszBrandingValueData1) == 0 && strcmp(buf2, g_lpszBrandingValueData2) == 0)
//						rc = false;
//				}
//			}
//		}
//	}
//	return rc;
//}


bool CXFormsXFormsElement::IsRepeatBeingWaitedFor(wchar_t *lpwszRepeat, int &nRepeats, bool bClear)
{
	bool rc = false;
	if(lpwszRepeat) {  // Bug 265
		POSITION p = m_lstRepeatsBeingWaitedFor.GetHeadPosition(), p2;
		wchar_t *buf;
		while(p) {
			p2 = p;
			buf = m_lstRepeatsBeingWaitedFor.GetNext(p);
			if(wcscmp(buf, lpwszRepeat) == 0) {
				rc = true;
				if(bClear) {
					delete[] buf;
					m_lstRepeatsBeingWaitedFor.RemoveAt(p2);
				}
				break;
			}
		}
		nRepeats = m_lstRepeatsBeingWaitedFor.GetCount();
	}
	return rc;
}
#define DEFERRED_UPDATE_MESSAGEPUMP 1
#undef DEFERRED_UPDATE_MESSAGEPUMP
#ifdef DEFERRED_UPDATE_MESSAGEPUMP
	const UINT g_msgDeferredUpdateRebuild = WM_APP + 2;
	const UINT g_msgDeferredUpdateRecalculate = g_msgDeferredUpdateRebuild + 1;
	const UINT g_msgDeferredUpdateRevalidate =  g_msgDeferredUpdateRecalculate+ 1;
	const UINT g_msgDeferredUpdateRefresh =  g_msgDeferredUpdateRevalidate+ 1;
#endif
// <summary>calls RRR functions on all models where needed</summary>
HRESULT CXFormsXFormsElement::RunDeferredUpdate(void)
{
	HRESULT hr = E_FAIL;
	//stealthily increase and decrease the count,
	//	to prohibit calls to increase/decrease
	//	inside RRRR processing from causing mad
	//	recursion
	++m_ulDeferredUpdateCnt;

#ifdef DEFERRED_UPDATE_MESSAGEPUMP
	MSG msg;
	 // force msg-q to be created just in case, NOP otherwise
    PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
    // msg to myself to do work
	DWORD dwThread = GetCurrentThreadId();
    PostThreadMessage(dwThread,g_msgDeferredUpdateRebuild, 0, 0);

    // msg-pump
    while (PeekMessage(&msg,NULL,NULL,NULL,PM_REMOVE))
    {
          // this was my message -- time to do my work
          if (msg.message == g_msgDeferredUpdateRebuild)
          {
				 hr = rebuild(); 
				if(SUCCEEDED(hr))
				    PostThreadMessage(dwThread,g_msgDeferredUpdateRecalculate, 0, 0);
          }
          else if ( msg.message == g_msgDeferredUpdateRecalculate)
          {
				hr = recalculate();
				if(SUCCEEDED(hr))
				    PostThreadMessage(dwThread,g_msgDeferredUpdateRevalidate, 0, 0);
		  }
          else if  (msg.message == g_msgDeferredUpdateRevalidate)
          {
				hr = revalidate();
				if(SUCCEEDED(hr))
				    PostThreadMessage(dwThread,g_msgDeferredUpdateRefresh, 0, 0);
		  }
          else if  (msg.message == g_msgDeferredUpdateRefresh)
          {
				hr = refresh();
				break;
		  }
		  else
          {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
          }
    }
#else
	hr = rebuild(); 
	if(SUCCEEDED(hr))
		hr = recalculate();
	if(SUCCEEDED(hr))
		hr = revalidate();
	if(SUCCEEDED(hr))
		hr = refresh();
#endif
	--m_ulDeferredUpdateCnt;
	return hr;
}

// <summary>Resolves the url of the current XForms document, and stores it in m_sCurrentURL</summary>
HRESULT CXFormsXFormsElement::ResolveDocumentURL(void)
{
	//clear the old value (if any)
	m_sCurrentURL.Empty();

	CComQIPtr<IHTMLDocument3> pDoc3 = m_pOwnerDocument;
	CComPtr<IHTMLElementCollection> pelColl;

	// First we see if there is a <base> in the document
	CComBSTR sBaseTagName(L"base");
	HRESULT hr  = pDoc3->getElementsByTagName(sBaseTagName, &pelColl);
	if (SUCCEEDED(hr) && pelColl)
	{
		//If we find a <base> then we use the last one,
		//	in case there are more than one
		long lCount = 0;
		hr = pelColl->get_length(&lCount);

		if (SUCCEEDED(hr) && lCount)
		{
			CComPtr<IDispatch> pIDisp;
			CComVariant vIndex(lCount - 1);
			
			hr = pelColl->item(vIndex, CComVariant(0l), &pIDisp);
			if (SUCCEEDED(hr) && pIDisp)
			{
				CComQIPtr<IHTMLElement> pBase = pIDisp;
				CComVariant v;	
				CComBSTR sAttrName(L"href");

				hr = pBase->getAttribute(sAttrName, 1, &v);
				if (SUCCEEDED(hr) && (v.vt == VT_BSTR))
				{
					m_sCurrentURL = v.bstrVal;
				}
			}
		}//if ( there are any <base> elements in the collection )
	}//if ( a collection was returned )

	
	// If there was no <base> element or @xml:base, or the base element was
	//	invalid, then we just use the document's URL.

	if (::isEmpty(m_sCurrentURL))
	{
		CComQIPtr<IHTMLDocument4> pDoc4 = m_pOwnerDocument;
		//Get unencoded, in case we are loading from a filesystem.
		if (pDoc4)
			hr = pDoc4->get_URLUnencoded(&m_sCurrentURL);
	}
	return hr;
}

// <summary>returns a string used to wrap repeat iterations using the current namespace declarations.  If none yet exists, creates one and returns it.</summary>
BSTR CXFormsXFormsElement::GetRepeatIterationWrapper(void)
{
	if(!m_sRepeatIterationWrapper)
	{
		//Get a namespace prefix with which to prefix the group element.
		CStringW wsRepeatIteration;
		//fix up the XML string to accomodate current namespace declarations
		wsRepeatIteration.Format(L"<%S:group class='%s'></%S:group>",m_sDefaultNamespacePrefix,cssClassRepeatItem,m_sDefaultNamespacePrefix);
		//cache the generated string.
		m_sRepeatIterationWrapper = wsRepeatIteration.AllocSysString();
	}
	//return a copy of the iteration string
	return m_sRepeatIterationWrapper.Copy();
}

void CXFormsXFormsElement::registerUnload()
{
	if(m_pOwnerDocument)
	{
		//	Find the containing window
		CComPtr<IHTMLWindow2> pWin;
		HRESULT hr = m_pOwnerDocument->get_parentWindow(&pWin);

		if (SUCCEEDED(hr))
		{
			//Create unload handler

			CComObject<COnUnloadHandler> * pEventSink = 0;

			hr = CComObject<COnUnloadHandler>::CreateInstance(&pEventSink);
			if (S_OK == hr)
			{
				CComQIPtr<IHTMLWindow3> pWin3(pWin);
				pEventSink->Init(this,pWin3);
			}
		}
	}
}

HRESULT CXFormsXFormsElement::IsOperatingWithinFrameset( bool & bResult) const
{
	HRESULT hr = E_FAIL;
	if(m_pOwnerDocument)
	{
		// Get the window of the document.
		CComPtr<IHTMLWindow2> pWnd2;
		hr = m_pOwnerDocument->get_parentWindow(&pWnd2);
		if(pWnd2 && SUCCEEDED(hr))
		{
			//The method formerly (and erroneously) returned true for all documents with 
			//	IFrames in them
			// the mechanism for defining whether the document was running within a frame 
			//	was to get the count of the frames within the top window

			//Get the parent of the document's parentWindow.
			CComPtr<IHTMLWindow2> pWndParent;
			hr = pWnd2->get_parent(&pWndParent);
			//The top window returns itself for parent, not NULL, so must check equality
			bResult = !(pWnd2 == pWndParent);
		}
	}
	return hr;
}
// <summary>reports an exception from GetErrorInfo</summary>
//<param name="hr">HRESULT  of the exception to report</param>
void CXFormsXFormsElement::reportException(HRESULT hr)
{
	CString s;
	s.Format("A fatal error occurred in formsPlayer: 0x%08x.\n", hr);
	CComPtr<IErrorInfo> pErrInfo;
	
	if(IS_FP_CODE(hr) && GetErrorInfo(0, &pErrInfo) == S_OK) 
	{
		CComBSTR s2;
		pErrInfo->GetDescription(&s2);
		CW2A s3(s2);
		s += s3;
	} else
		s += "[unknown error]";
	
	reportException(s);

}

//<summary>reports a non-blocking error from GetErrorInfo</summary>
//<param name="hr">HRESULT  of the error to report</param>
void CXFormsXFormsElement::reportError(HRESULT hr)
{
	CString s;
	s.Format("formsPlayer warning: 0x%08x.\n", hr);
	CComPtr<IErrorInfo> pErrInfo;
	
	if(IS_FP_CODE(hr) && GetErrorInfo(0, &pErrInfo) == S_OK) 
	{
		CComBSTR s2;
		pErrInfo->GetDescription(&s2);
		CW2A s3(s2);
		s += s3;
	} else
		s += "\n[unknown error]";
	
	reportError(s);
}

HRESULT CXFormsXFormsElement::createNSResolver()
{
	CComPtr<IXMLDOMDocument2> pDocNSResolver;
	HRESULT hr = CoCreateInstance
	(
		CLSID_DOMDocument60,/* formerly FreeThreaded, but no need*/
		NULL,
		CLSCTX_SERVER,
		IID_IXMLDOMDocument2,
		(void**) &pDocNSResolver
	);
	if(SUCCEEDED(hr))
	{
		CComBSTR sNSNode(L"<x ");
		sNSNode += m_sNamespaceDeclaration;
		sNSNode += " />";
		VARIANT_BOOL bSuccess;
		hr = pDocNSResolver->loadXML(sNSNode,&bSuccess);
		//I don't know why these brances are commented
		//I think that we may wish to create the NS resolver,
		//	even if there is a problem above. 
//		if(hr == S_OK)
		{
			theApp.m_pEvaluator->createNSResolver(pDocNSResolver,&m_pNSResolver);
		}
//		else
		{
			
		}
	}
	return hr;
}

void CXFormsXFormsElement::loadConfigFile()
{
	//Load the initialisation file.
	//Get the path to the module, In the same directory should live 
	//	a file called formsplayer.xml, this will provide initialisation settings

	char fullpath[MAX_PATH];
	::GetModuleFileName(_Module.m_hInst,fullpath,MAX_PATH);
	
	//This is not strictly reliable, must find a way to get the full path
	//	not the shortened name.
	char * fileName = strrchr(fullpath,'\\');
	if(fileName)
	{
		//increment filename to after the directory separator
		++fileName;
		strcpy(fileName,"formsplayer.xml");
		CComBSTR bsFileName(fullpath);
		VARIANT_BOOL BLoaded;
		
		if(FAILED(m_Config.m_pXMLParser->load(_variant_t(bsFileName),&BLoaded)) || !BLoaded)
		{
			//Not really an error, but important to log it for debugging.
			AtlTrace("'formsplayer.dll': failed to load configuration file\n");
		}
	}
	else
	{
			AtlTrace("'formsplayer.dll': loadConfigFile - could not resolve home directory\n");		
	}

}


HRESULT GetInlineBindings(IHTMLDocument3 * pDoc3,IXMLDOMNodeList ** ppNl)
{
	HRESULT hr = S_FALSE;
	*ppNl = 0;
	CComPtr<IHTMLElement> pEl;
	CComPtr<IHTMLElementCollection> pColl;
	pDoc3->getElementsByTagName(CComBSTR(L"binding"),&pColl);
	if(pColl)
	{
		long l;
		pColl->get_length(&l);
		if(l)
		{
			CComVariant vt0(0l);
			CHTMLDOMTranslator translator(CLSID_DOMDocument60);
			CComPtr<IXMLDOMDocument> pDoc;
			pDoc.CoCreateInstance(CLSID_DOMDocument60);
			if(pDoc)
			{
				VARIANT_BOOL bSuccess;
				pDoc->loadXML(CComBSTR(L"<x />"),&bSuccess);
				CComPtr<IXMLDOMElement> pDocEl;
				pDoc->get_documentElement(&pDocEl);
				if(pDocEl)
				{
					for(long i = 0;i<l;++i)
					{
						CComPtr<IDispatch> pDispEl;
						pColl->item(CComVariant(i),vt0,&pDispEl);
						CComQIPtr<IHTMLDOMNode> pHTMLNode = pDispEl;
						if(pHTMLNode)
						{
							CComPtr<IXMLDOMNode> pXMLNode;
							hr = translator.cloneNode(pHTMLNode,&pXMLNode,true);
							if(pXMLNode)
								pDocEl->appendChild(pXMLNode,0);
						}
					}
					hr = pDocEl->get_childNodes(ppNl);
				}
			}

		}
	}
	return hr;

}

CUIImplRepository * CXFormsXFormsElement::GetUIImplRepository(void)
{
	if(!m_pUIImplRepository)
	{
		CComBSTR bsHome;
		theApp.FormsManager().GetFPHomeDirectory(&bsHome);
		bsHome += L"\\chrome\\";

		CComQIPtr<IXSLProcessor> pProcessor;
		CComPtr<IXMLDOMNodeList> pNlBindings;

		CComQIPtr<IHTMLDocument3> pDoc3 = m_pOwnerDocument;
		if(pDoc3)
		{
			
			GetInlineBindings(pDoc3,&pNlBindings);
		}
		CComPtr<IDOMImplementation> pImpl;
		m_pDocImpl->GetImplementation(&pImpl);
		m_pUIImplRepository = new CUIImplRepository(bsHome,pNlBindings,pProcessor,pImpl);
		//Add additional link elements.
		if(pDoc3)
		{
			CComBSTR sLink(L"link");
			CComPtr<IHTMLElementCollection> pColl;
			pDoc3->getElementsByTagName(sLink,&pColl);
			if(pColl)
			{
				long l;
				pColl->get_length(&l);
				CComVariant vtZero(0l);
				for(long i = 0;i<l;++i)
				{
					CComPtr<IDispatch> pDisp;
					pColl->item(CComVariant(i),vtZero,&pDisp);
					CComQIPtr<IHTMLLinkElement> pLink = pDisp;
					if(pLink)
					{
						CComBSTR bsRel;
						pLink->get_rel(&bsRel);
						if(bsRel == L"bindings")
						{
							CComBSTR bsHref;
							pLink->get_href(&bsHref);
							CComBSTR bsFullPath;
							bsFullPath.Attach(::Relative2AbsolutePath(m_sCurrentURL,bsHref,false));
							m_pUIImplRepository->AddBindingsDocument(bsFullPath);
						}

					}
				}
			}
			
		}
	}


	return m_pUIImplRepository;
}


HRESULT CXFormsXFormsElement::GetOwnerDocument(IHTMLDocument2 ** ppDoc)
{
	HRESULT hr = m_pOwnerDocument?S_OK:E_FAIL;
	if(SUCCEEDED(hr))
		m_pOwnerDocument.CopyTo(ppDoc);
	return hr;
}

// <summary>indicates whether a modal message is active right now, so that an action may be deferred until after it has returned.</summary>
bool CXFormsXFormsElement::modalMessageActive(void)
{
	return m_bModalMessageActive;
}

// <summary>When a modal message is active, this stores any actions posted after it, so that they can be invoked on return of the message.</summary>
HRESULT CXFormsXFormsElement::AddPostModalAction(IAction * pAction)
{
	HRESULT hr = S_FALSE;
	if(pAction)
	{
		m_lstPostModalActions.AddTail(pAction);
		hr = S_OK;
	}
	return hr;
}

HRESULT CXFormsXFormsElement::ExecutePostModalActions()
{
	//If a modal message occurs in this list, it will reset m_bModalMessageActive.
	//	At that point, we should stop executing again.	
	while(!m_lstPostModalActions.IsEmpty() && !m_bModalMessageActive)
	{
		m_lstPostModalActions.RemoveHead()->Invoke();
	}

	if(m_lstPostModalActions.IsEmpty())
	{
		//Clear any used error reports
		m_ErrorActionFactory.Clear();
	}
	return S_OK;
}

void CXFormsXFormsElement::activateModalMessage()
{
	m_bModalMessageActive = true;
}

HRESULT CXFormsXFormsElement::deactivateModalMessage()
{
	m_bModalMessageActive = false;
	return ExecutePostModalActions();
}

HRESULT CXFormsXFormsElement::put_Factory(IFactory * pFac)
{
	HRESULT hr = S_FALSE;
	if(!m_pFactory)
	{
		m_pFactory = pFac;
		hr = m_pFactory?S_OK:E_NOINTERFACE;
	}
	return hr;
}

// <summary>returns the requested interfaace of the xforms factory object in the document</summary>
HRESULT CXFormsXFormsElement::GetElementFactory(const IID * piid, void ** ppFactory)
{
	HRESULT hr = E_PENDING;

	if(m_pFactory)
	{
		hr = m_pFactory->QueryInterface(*piid,ppFactory);
	}
	return hr;
}

HRESULT CXFormsXFormsElement::GetControl(const CFormControl **pp, bool bStart)
{
	HRESULT hr = S_FALSE;
	if(pp) {
		if(bStart)
			m_ControlsIndex = 0;
		if(m_ControlsIndex < m_ControlsVector.size()) {
			*pp = m_ControlsVector.at(m_ControlsIndex++);
			hr = S_OK;
		}
	} else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsXFormsElement::GetControl");
	}
	return hr;
}

HRESULT CXFormsXFormsElement::AddControl(const CFormControl *p)
{
	HRESULT hr = S_OK;
	if(p)
		m_ControlsVector.push_back(p);
	else {
		hr = FP_E_BADARG;
		SetupErrorInfo(hr, L"CXFormsXFormsElement::AddControl");
	}
	return hr;
}
/*
void CXFormsXFormsElement::LegacyFixupErrorMessage(const char * const sMessage, BSTR *pbsRetval)
{
	CStringW csErr("<style>body, pre{background:#ffffff;font-family:\"Trebuchet MS\", Verdana, Sans-Serif; font-size:x-small;}</style><img src=\"%FORMSPLAYER_PATH%/fplogo80x60.jpg\" style=\"float:left;\"/>");
	insertResourceLocation(csErr,false);
	csErr += sMessage;
	csErr.Replace(L"\n",L"<br />");
	*pbsRetval = csErr.AllocSysString();
}
*/
HRESULT CXFormsXFormsElement::LegacyMessage(BSTR sText, bool bNavigate)
{
	return modalMessage(sText, bNavigate);
}

HRESULT CXFormsXFormsElement::findModel(IXMLDOMNode * pNode, CImplModel ** ppModel) {
	HRESULT hr = S_FALSE;
	// Lets check the parameters
	if(pNode && ppModel && !*ppModel) {
		// Get the document the node was inserted into.
		CComPtr<IXMLDOMDocument> pDoc;
		pNode->get_ownerDocument(&pDoc);
		if(pDoc) {
            CComPtr<IUnknown> pUnkDoc;
			pDoc->QueryInterface(__uuidof(IUnknown), (void **) &pUnkDoc);
			if(pUnkDoc) {
				// And retreive a pointer to the xf:model which has this 
				// document in one of its xf:instance's
				 getModel(pUnkDoc, *ppModel);
			} else {
				E_FAIL;
			}
		} else {
			hr = E_FAIL;
		}
	} else {
		hr = E_INVALIDARG;
	}

	return hr;
}

void CXFormsXFormsElement::PushCurrentXPathContext(CNode * pContextInfo,CImplModel * pModel)
{
	if(m_pFuncLib)
		m_pFuncLib->PushCurrentXPathContext(pContextInfo,pModel);
}
void CXFormsXFormsElement::PopCurrentXPathContext()
{
	if(m_pFuncLib)
		m_pFuncLib->PopCurrentXPathContext();
}


HRESULT CXFormsXFormsElement::LoadScriptLibrary(IScriptEngine2 * pScript2,CString sPathToScript)
{
	char * pcScriptFileContent = 0;
	HRESULT hr = xport::ReadFromFileSystem(sPathToScript,reinterpret_cast<void**>(&pcScriptFileContent));
	if(SUCCEEDED(hr))
	{
		CComBSTR bsScript;
		bsScript = pcScriptFileContent;
		hr = pScript2->addScriptText(bsScript);
	}
	free(pcScriptFileContent);
	return hr;
}

HRESULT CXFormsXFormsElement::PrepareNewScriptLibrary(IScriptEngine ** ppLibraryToCreate)
{
	CComPtr<IScriptEngine> pLibraryToCreate;
	HRESULT hr = pLibraryToCreate.CoCreateInstance(CLSID_ScriptEngine);
	if(SUCCEEDED(hr)&& pLibraryToCreate)
	{
		//loadScriptText is needed first to create the environment
		hr = pLibraryToCreate->loadScriptText(L"JScript",L"function alert(s){window.alert(s);}");
		if(SUCCEEDED(hr))
		{
			CComBSTR sDoc(L"document");
			hr = pLibraryToCreate->AddNamedItem(sDoc,m_pOwnerDocument);
			if(SUCCEEDED(hr))
			{
				CComPtr<IHTMLWindow2> pWin2;
				m_pOwnerDocument->get_parentWindow(&pWin2);
				if(pWin2)
				{
					CComBSTR sWindow(L"window");
					hr = pLibraryToCreate->AddNamedItem(sWindow,pWin2);
					if(SUCCEEDED(hr))
					{
						CComQIPtr<IScriptEngine2> pScript2(pLibraryToCreate);
						hr = pScript2->AddGlobalMembers(pWin2);
						if(SUCCEEDED(hr))
						{
							pLibraryToCreate.CopyTo(ppLibraryToCreate);
						}
					}
				}
			}
		}
	}
	return hr;

}

HRESULT CXFormsXFormsElement::CreateRicoLibrary()
{
	HRESULT hr = PrepareNewScriptLibrary(&m_pRico);
	if(SUCCEEDED(hr) && m_pRico)
	{
		CComBSTR sDirectory;
		theApp.FormsManager().GetFPHomeDirectory(&sDirectory);
		
		CComQIPtr<IScriptEngine2> pScript2 = m_pRico;

		CString sPathToScript(sDirectory);
		sPathToScript += "\\chrome\\rico\\prototype.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);


		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\rico\\rico.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);
		if(SUCCEEDED(hr))
		{
			CComBSTR sRico(L"Rico");
			hr = AddScriptingObjectToDocument(sRico,m_pRico,m_pOwnerDocument);
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::CreatePanelLibrary()
{
	HRESULT hr = PrepareNewScriptLibrary(&m_pPanel);
	if(SUCCEEDED(hr) && m_pPanel)
	{
		CComBSTR sDirectory;
		theApp.FormsManager().GetFPHomeDirectory(&sDirectory);
		
		CComQIPtr<IScriptEngine2> pScript2 = m_pPanel;

		CString sPathToScript(sDirectory);
		sPathToScript += "\\chrome\\YUI\\yahoo-dom-event.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);


		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\YUI\\container-min.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);

		if(SUCCEEDED(hr))
		{
			CComBSTR sPanel(L"PANEL");
			hr = AddScriptingObjectToDocument(sPanel,m_pPanel,m_pOwnerDocument);
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::CreateScriptaculousLibrary()
{
	HRESULT hr = PrepareNewScriptLibrary(&m_pScriptaculous);
	if(SUCCEEDED(hr) && m_pScriptaculous)
	{
		CComBSTR sDirectory;
		theApp.FormsManager().GetFPHomeDirectory(&sDirectory);
		
		CComQIPtr<IScriptEngine2> pScript2 = m_pScriptaculous;

		CString sPathToScript(sDirectory);
		sPathToScript += "\\chrome\\scriptaculous\\prototype.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);


		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\scriptaculous\\builder.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);
		
		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\scriptaculous\\effects.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);

		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\scriptaculous\\dragdrop.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);

		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\scriptaculous\\controls.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);
		
		sPathToScript = sDirectory;
		sPathToScript += "\\chrome\\scriptaculous\\slider.js";
		hr = LoadScriptLibrary(pScript2,sPathToScript);

		if(SUCCEEDED(hr))
		{
			CComBSTR sScriptaculous(L"Scriptaculous");
			hr = AddScriptingObjectToDocument(sScriptaculous,m_pScriptaculous,m_pOwnerDocument);
		}

	}
	return hr;
}

HRESULT CXFormsXFormsElement::CreateSelect1MinimalLibrary()
{
	HRESULT hr = PrepareNewScriptLibrary(&m_pSelect1MinimalLibrary);
	if(SUCCEEDED(hr) && m_pSelect1MinimalLibrary)
	{
		CComQIPtr<IScriptEngine2> pScript2 = m_pSelect1MinimalLibrary;
		CComBSTR sDirectory;
		theApp.FormsManager().GetFPHomeDirectory(&sDirectory);

		CComBSTR bsScript;
		xport::loadHTMLResource(::_Module.m_hInst,"s1min.js", bsScript);
		hr = pScript2->addScriptText(bsScript);

		if(SUCCEEDED(hr))
		{
			m_pSelect1MinimalLibrary->GetScriptObject(&m_pSelect1MinimalLibraryDispatch);
			hr = AddScriptingObjectToDocument(L"S1Min",m_pSelect1MinimalLibrary,m_pOwnerDocument);
		}
	}
	return hr;
}

HRESULT CXFormsXFormsElement::RemoveScriptingObjectFromDocument(BSTR bsScriptObjectName,IHTMLDocument2 * pDoc)
{
	CComQIPtr<IDispatchEx> pDocDispEx = pDoc;
	DISPID dispid = 0;
	HRESULT hr = pDocDispEx->GetDispID(bsScriptObjectName,fdexNameCaseInsensitive ,&dispid);
	if(dispid)
	{
		CComVariant vDisp = 0;
		DISPID putid = DISPID_PROPERTYPUT;
		DISPPARAMS dparams;
		dparams.rgvarg = &vDisp;
		dparams.rgdispidNamedArgs = &putid;
		dparams.cArgs = 1;
		dparams.cNamedArgs = 1;
		pDocDispEx->InvokeEx(dispid, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dparams, 0, 0, 0);
	}
	return hr;
}

HRESULT CXFormsXFormsElement::AddScriptingObjectToDocument(BSTR bsScriptObjectName, IScriptEngine * pScript,IHTMLDocument2 * pDoc)
{
	CComPtr<IDispatch> pDisp;
	HRESULT hr = pScript->GetScriptObject(&pDisp);
	
	CComQIPtr<IDispatchEx> pDocDispEx = pDoc;
	DISPID dispid = 0;
	hr = pDocDispEx->GetDispID(bsScriptObjectName,fdexNameEnsure,&dispid);
	if(dispid)
	{
		CComVariant vDisp = pDisp;
		DISPID putid = DISPID_PROPERTYPUT;
		DISPPARAMS dparams;
		dparams.rgvarg = &vDisp;
		dparams.rgdispidNamedArgs = &putid;
		dparams.cArgs = 1;
		dparams.cNamedArgs = 1;
		pDocDispEx->InvokeEx(dispid, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYPUT, &dparams, 0, 0, 0);

	}
	return hr;
}

HRESULT CXFormsXFormsElement::RemoveScriptLibrariesFromViewlinkDocument(IHTMLDocument2 * pDoc2)
{
	CComBSTR sScriptaculous(L"Scriptaculous");
	HRESULT hr = RemoveScriptingObjectFromDocument(sScriptaculous,pDoc2);
	return hr;	
}

HRESULT CXFormsXFormsElement::AddScriptLibrariesToViewlinkDocument(IHTMLDocument2 * pDoc2)
{
	HRESULT hr = E_PENDING;
	if(m_pScriptaculous)
	{
		CComBSTR sScriptaculous(L"Scriptaculous");
		hr = AddScriptingObjectToDocument(sScriptaculous,m_pScriptaculous,pDoc2);
	}

	if(m_pRico)
	{
		CComBSTR sRico(L"Rico");
		hr = AddScriptingObjectToDocument(sRico,m_pScriptaculous,pDoc2);	
	}

	return hr;
}

HRESULT CXFormsXFormsElement::addDefaultStylesheet()
{
	HRESULT hr = E_PENDING;
	if(m_pOwnerDocument)
	{
		char fullpath[MAX_PATH + 1];
		::GetModuleFileName(_Module.m_hInst,fullpath,MAX_PATH);

		char * fileName = strrchr(fullpath,'\\');
		if(fileName)
		{
			//increment filename to after the directory separator
			++fileName;
			strcpy(fileName,"chrome/");//"default.css");

			CString sChromeDir;
			sChromeDir.Format("file://%s",fullpath);
			sChromeDir.Replace("\\","/");

			strcpy(&(fileName[7]),"default.css");
			try
			{
				m_pCSSHandler = new CMSHTMLCSS3Handler(m_pOwnerDocument,fullpath,sChromeDir);
				CComBSTR bsPrefix(m_sDefaultNamespacePrefix);
				hr = m_pCSSHandler->AddNamespacePrefix(bsPrefix);
			}
			catch(HRESULT _hr)
			{
				hr = _hr;
			}

			if(m_pCSSHandler)
			{
				CComBSTRList::iterator i = m_lstNamespacePrefixesWhichCorrespondToTheXFormsNamespace.begin();
				while(i != m_lstNamespacePrefixesWhichCorrespondToTheXFormsNamespace.end())
				{
					m_pCSSHandler->AddNamespacePrefix(*i);
					++i;
				}
			}
			//This list is no longer required.
			m_lstNamespacePrefixesWhichCorrespondToTheXFormsNamespace.clear();
		}
	}
	return hr;
}
HRESULT CXFormsXFormsElement::getUIChunk(BSTR m_sControlName,SAFEARRAY ** ppsfArray)
{
	HRESULT hr;
	CMapCComBSTRToSafeArray::const_iterator iterator;
	iterator = m_mapUIChunks.find(m_sControlName);
	if(iterator == m_mapUIChunks.end())
	{
		*ppsfArray = 0;
		hr = E_FAIL;
	}
	else
	{
		*ppsfArray = iterator->second;
		hr = S_OK;
	}
	return hr;
}

HRESULT CXFormsXFormsElement::addUIChunk(BSTR m_sControlName,SAFEARRAY * sfArray)
{
	HRESULT hr = S_OK;
	m_mapUIChunks.insert(CComBSTRSafeArrayPair(m_sControlName,sfArray));
	return hr;

}

void CXFormsXFormsElement::destroyUIChunks()
{
	CMapCComBSTRToSafeArray::const_iterator iterator = m_mapUIChunks.begin();
	while(iterator !=m_mapUIChunks.end())
	{
		SAFEARRAY * sfArray = iterator->second;
		::SafeArrayUnaccessData(sfArray);		
		::SafeArrayDestroy(sfArray);
		::SafeArrayDestroyData(sfArray);
		++iterator;
	}
	
	m_mapUIChunks.clear();

}

void CXFormsXFormsElement::AddControlAwaitingFocus(CFormControl * pControl)
{
	m_ControlsAwaitingFocus.push_front(pControl);
}

HRESULT CXFormsXFormsElement::FocusControlAwaitingFocus()
{
	HRESULT hr = S_FALSE;
	CFormControlList::iterator i = m_ControlsAwaitingFocus.begin();
	while(i != m_ControlsAwaitingFocus.end())
	{
		hr = (*i)->setFocus();
		if(S_OK == hr)
			break;
		++i;
	}
	m_ControlsAwaitingFocus.erase(m_ControlsAwaitingFocus.begin(),m_ControlsAwaitingFocus.end());
	return hr;
}
#include "ValueElementStringGenerator.h"
HRESULT CXFormsXFormsElement::GetValueElementString(BSTR * pbsValueElement)
{
	HRESULT hr;
	if(!m_pValueElementStringGenerator)
	{
		CComBSTR s;
		s.Attach(m_sDefaultNamespacePrefix.AllocSysString());
		m_pValueElementStringGenerator	= new CValueElementStringGenerator(s);
	}

	if(m_pValueElementStringGenerator)
	{
		hr = S_OK;
		m_pValueElementStringGenerator->GetValueElementString(pbsValueElement);
	}
	else
	{
		hr = E_FAIL;
	}
	return hr;
}

bool CXFormsXFormsElement::CreatePIP(void)
{
	if(xport::RunningOnTabletPC() && !m_pPIP && m_pOwnerDocument) {
		CComPtr<IHTMLWindow2> pHTMLWindow;
		m_pOwnerDocument->get_parentWindow(&pHTMLWindow);
		CComQIPtr<IServiceProvider> pServiceProvider = pHTMLWindow;
		if(pServiceProvider) {
			CComPtr<IOleWindow> pOLEWindow;
			pServiceProvider->QueryService(SID_SShellBrowser, IID_IOleWindow, reinterpret_cast<void **>(&pOLEWindow));
			if(pOLEWindow) {
				HWND hHTMLWindow = 0;
				pOLEWindow->GetWindow(&hHTMLWindow);
				if(hHTMLWindow != 0) {
					xport::CreatePIP(m_pPIP);
					if(m_pPIP)
						xport::InitialisePIP(m_pPIP, hHTMLWindow);
				}
			}
		}
	}

	return m_pPIP != 0;
}

void CXFormsXFormsElement::ShowTIP(void)
{
	xport::ShowTIP(m_pTIP);
}

void CXFormsXFormsElement::HideTIP(void)
{
	xport::HideTIP(m_pTIP);
}

void CXFormsXFormsElement::ShowPIP(void)
{
	if(CreatePIP())
		m_pPIP->put_Visible(VARIANT_TRUE);
}

void CXFormsXFormsElement::HidePIP(void)
{
	if(CreatePIP())
		m_pPIP->put_Visible(VARIANT_FALSE);
}

void CXFormsXFormsElement::AddPendingRepeat(BSTR s)
{
	m_lstPendingRepeats.push_back(s);
}

bool CXFormsXFormsElement::IsPendingRepeat(const BSTR s)const
{
	CComBSTRList::const_iterator i = m_lstPendingRepeats.begin();
	while(i != m_lstPendingRepeats.end())
	{
		if((*i) == s)
		{
			return true;
		}
		++i;
	}
	return false;
}


void CXFormsXFormsElement::AddDeletionListener( IDeletionListener * pListener)
{
	m_lstDeletionListeners.push_back(pListener);
}

void CXFormsXFormsElement::RemoveDeletionListener( IDeletionListener *  pListener)
{
	m_lstDeletionListeners.remove(pListener);
}

void CXFormsXFormsElement::NotifyDeletionListeners()
{
	CListOfDeletionListeners::iterator i = m_lstDeletionListeners.begin();
	while(i != m_lstDeletionListeners.end())
	{
		(*i)->OnDeleteOfSubject();
		++i;
	}
	
}

void CXFormsXFormsElement::FlushDeletionListeners()
{
	m_lstDeletionListeners.clear();
}
